xref: /illumos-gate/usr/src/uts/sun4u/io/rmc_comm_drvintf.c (revision 6fa6856ee31afb8f296ddb9f5201cfc9cc5ad729)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*
28  * provide the interface to the layered drivers (send request/receive
29  * response to the RMC
30  *
31  */
32 
33 #pragma ident	"%Z%%M%	%I%	%E% SMI"
34 
35 /*
36  *  Header files
37  */
38 #include <sys/conf.h>
39 #include <sys/callb.h>
40 #include <sys/cyclic.h>
41 #include <sys/membar.h>
42 #include <sys/modctl.h>
43 #include <sys/strlog.h>
44 #include <sys/sunddi.h>
45 #include <sys/ddi.h>
46 #include <sys/types.h>
47 #include <sys/disp.h>
48 #include <sys/rmc_comm_dp.h>
49 #include <sys/rmc_comm_dp_boot.h>
50 #include <sys/rmc_comm_drvintf.h>
51 #include <sys/rmc_comm.h>
52 
53 void dp_reset(struct rmc_comm_state *, uint8_t, boolean_t, boolean_t);
54 void dp_wake_up_waiter(struct rmc_comm_state *, uint8_t);
55 
56 static int rmc_comm_send_req_resp(struct rmc_comm_state *rcs,
57     rmc_comm_msg_t *request, rmc_comm_msg_t *response, uint32_t wait_time);
58 static int rmc_comm_wait_bp_reply(struct rmc_comm_state *,
59     rmc_comm_dp_state_t *, dp_req_resp_t *, clock_t);
60 static void rmc_comm_wait_enable_to_send(struct rmc_comm_state *,
61     rmc_comm_dp_state_t *);
62 static void rmc_comm_wake_up_next(struct rmc_comm_state *);
63 static void rmc_comm_send_pend_req(caddr_t arg);
64 static int rmc_comm_dreq_thread_start(struct rmc_comm_state *rcs);
65 static void rmc_comm_dreq_thread_kill(struct rmc_comm_state *rcs);
66 
67 /*
68  * leaf driver to use this function to send a request to the remote side (RMC)
69  * and wait for a reply
70  */
71 int
72 rmc_comm_request_response(rmc_comm_msg_t *request,
73     rmc_comm_msg_t *response, uint32_t wait_time)
74 {
75 	struct rmc_comm_state	*rcs;
76 
77 	/*
78 	 * get the soft state struct (instance 0)
79 	 */
80 	if ((rcs = rmc_comm_getstate(NULL, 0,
81 				"rmc_comm_request_response")) == NULL)
82 		return (RCENOSOFTSTATE);
83 
84 	return (rmc_comm_send_req_resp(rcs, request, response, wait_time));
85 }
86 
87 /*
88  * leaf driver to use this function to send a request to the remote side (RMC)
89  * without waiting for a reply. If flag is RMC_COMM_DREQ_URGENT, the request
90  * message is sent once-off (an eventual pending request is aborted). This
91  * flag must only be used when try to send a request in critical condition
92  * (while the system is shutting down for instance and the CPU signature
93  * has to be sent). Otherwise, the request is stored in a temporary location
94  * and delivered by a thread.
95  */
96 int
97 rmc_comm_request_nowait(rmc_comm_msg_t *request, uint8_t flag)
98 {
99 	struct rmc_comm_state		*rcs;
100 	rmc_comm_dp_state_t		*dps;
101 	rmc_comm_drvintf_state_t	*dis;
102 	dp_message_t			req;
103 	int				err = RCNOERR;
104 	uint8_t				flags = 0;
105 
106 	/*
107 	 * get the soft state struct (instance 0)
108 	 */
109 	if ((rcs = rmc_comm_getstate(NULL, 0,
110 				"rmc_comm_request_response")) == NULL)
111 		return (RCENOSOFTSTATE);
112 
113 	/*
114 	 * just a sanity check...
115 	 */
116 	if (request == NULL) {
117 		DPRINTF(rcs, DAPI, (CE_CONT, "reqnowait, invalid args\n"));
118 		return (RCEINVARG);
119 	}
120 
121 	if (!IS_NUMBERED_MSG(request->msg_type)) {
122 		DPRINTF(rcs, DAPI, (CE_CONT,
123 		    "reqnowait, ctrl msg not allowed! req type=%x\n",
124 		    request->msg_type));
125 		return (RCEINVARG);
126 	}
127 
128 	if (flag == RMC_COMM_DREQ_URGENT) {
129 		/*
130 		 * Send this request with high priority i.e. abort eventual
131 		 * request/response pending sessions.
132 		 */
133 
134 		dps = &rcs->dp_state;
135 
136 		DPRINTF(rcs, DAPI, (CE_CONT, "going to send request=%x (URG)\n",
137 		    request->msg_type));
138 
139 		/*
140 		 * Handle the case where we are called during panic
141 		 * processing.  If that occurs, then another thread in
142 		 * rmc_comm might have been idled by panic() while
143 		 * holding dp_mutex.  As a result, do not unconditionally
144 		 * grab dp_mutex.
145 		 */
146 		if (ddi_in_panic() != 0) {
147 			if (mutex_tryenter(dps->dp_mutex) == 0) {
148 				return (RCENODATALINK);
149 			}
150 		} else {
151 			mutex_enter(dps->dp_mutex);
152 		}
153 
154 		/*
155 		 * send the request only if the protocol data link is up.
156 		 * it is pointless to send it in the other case.
157 		 */
158 		if (dps->data_link_ok) {
159 
160 			/*
161 			 * clean up an eventual pending request/response session
162 			 * (save its current status)
163 			 */
164 			if (dps->pending_request) {
165 				flags = dps->req_resp.flags;
166 				rmc_comm_dp_mcleanup(rcs);
167 			}
168 
169 			/*
170 			 * send the request message
171 			 */
172 			req.msg_type = request->msg_type;
173 			req.msg_buf = (uint8_t *)request->msg_buf;
174 			req.msg_msglen = (uint16_t)request->msg_len;
175 
176 			DPRINTF(rcs, DAPI, (CE_CONT, "send request=%x (URG)\n",
177 			    request->msg_type));
178 
179 			err = rmc_comm_dp_msend(rcs, &req);
180 
181 			/*
182 			 * wait for fifos to drain
183 			 */
184 			rmc_comm_serdev_drain(rcs);
185 
186 			/*
187 			 * clean up the current session
188 			 */
189 			rmc_comm_dp_mcleanup(rcs);
190 
191 			/*
192 			 * abort an old session (if any)
193 			 */
194 			if (dps->pending_request) {
195 				dps->req_resp.flags = flags;
196 				dp_wake_up_waiter(rcs, MSG_ERROR);
197 			}
198 		}
199 
200 		mutex_exit(dps->dp_mutex);
201 
202 	} else {
203 
204 		/*
205 		 * Get an 'independent' thread (rmc_comm_send_pend_req)
206 		 * to send this request (since the calling thread does not
207 		 * want to wait). Copy the request in the drvintf state
208 		 * structure and signal the thread.
209 		 */
210 
211 		dis = &rcs->drvi_state;
212 
213 		mutex_enter(dis->dreq_mutex);
214 
215 		if (dis->dreq_state == RMC_COMM_DREQ_ST_WAIT) {
216 
217 			DPRINTF(rcs, DAPI, (CE_CONT, "get to send request=%x\n",
218 			    request->msg_type));
219 
220 			/*
221 			 * copy the request in a temporary location
222 			 * (drvinf_state structure) and signal the thread
223 			 * that a request message has to be delivered
224 			 */
225 
226 			if (request->msg_len < DP_MAX_MSGLEN) {
227 				dis->dreq_request.msg_type = request->msg_type;
228 				dis->dreq_request.msg_len = request->msg_len;
229 				dis->dreq_request.msg_buf =
230 				    dis->dreq_request_buf;
231 				bcopy(request->msg_buf,
232 				    dis->dreq_request.msg_buf,
233 				    request->msg_len);
234 
235 				dis->dreq_state = RMC_COMM_DREQ_ST_PROCESS;
236 				cv_signal(dis->dreq_sig_cv);
237 
238 			} else {
239 				/*
240 				 * not enough space to hold the request
241 				 */
242 				err = RCEREPTOOBIG;
243 			}
244 		} else {
245 
246 			DPRINTF(rcs, DAPI, (CE_CONT, "cannot get to send "
247 			    "request=%x (busy)\n", request->msg_type));
248 
249 			/*
250 			 * only one request per time can be processed.
251 			 * the thread is either busy (RMC_COMM_DREQ_ST_PROCESS)
252 			 * or terminating (RMC_COMM_DREQ_ST_EXIT)
253 			 */
254 			err = RCEGENERIC;
255 		}
256 
257 		mutex_exit(dis->dreq_mutex);
258 	}
259 
260 	return (err);
261 }
262 
263 /*
264  * Function used to send a request and (eventually) wait for a response.
265  * It can be called from a leaf driver (via rmc_comm_request_response) or
266  * from the thread in charge of sending 'no-wait' requests
267  * (rmc_comm_send_pend_req).
268  */
269 static int
270 rmc_comm_send_req_resp(struct rmc_comm_state *rcs, rmc_comm_msg_t *request,
271     rmc_comm_msg_t *response, uint32_t wait_time)
272 {
273 	rmc_comm_dp_state_t	*dps;
274 	dp_req_resp_t		*drr;
275 	dp_message_t		*exp_resp;
276 	dp_message_t		req;
277 	clock_t			resend_clockt;
278 	clock_t			stop_clockt;
279 	int			err;
280 
281 
282 	/*
283 	 * just a sanity check...
284 	 */
285 	if (request == NULL) {
286 		DPRINTF(rcs, DAPI, (CE_CONT, "reqresp, invalid args\n"));
287 		return (RCEINVARG);
288 	}
289 
290 	/*
291 	 * drivers cannot send control messages at all. They are meant to
292 	 * be used at low level only.
293 	 */
294 	if (!IS_NUMBERED_MSG(request->msg_type)) {
295 		DPRINTF(rcs, DAPI, (CE_CONT,
296 		    "reqresp, ctrl msg not allowed! req type=%x\n",
297 		    request->msg_type));
298 		return (RCEINVARG);
299 	}
300 
301 	dps = &rcs->dp_state;
302 	drr = &dps->req_resp;
303 	exp_resp = &drr->response;
304 
305 	/*
306 	 * Handle the case where we are called during panic
307 	 * processing.  If that occurs, then another thread in
308 	 * rmc_comm might have been idled by panic() while
309 	 * holding dp_mutex.  As a result, do not unconditionally
310 	 * grab dp_mutex.
311 	 */
312 	if (ddi_in_panic() != 0) {
313 		if (mutex_tryenter(dps->dp_mutex) == 0) {
314 			return (RCENODATALINK);
315 		}
316 	} else {
317 		mutex_enter(dps->dp_mutex);
318 	}
319 
320 	/*
321 	 * if the data link set up is suspended, just return.
322 	 * the only time that this can happen is during firmware download
323 	 * (see rmc_comm_request_response_bp). Basically, the data link is
324 	 * down and the timer for setting up the data link is not running.
325 	 */
326 	if (!dps->data_link_ok &&
327 	    dps->timer_link_setup == (timeout_id_t)0) {
328 
329 		mutex_exit(dps->dp_mutex);
330 		return (RCENODATALINK);
331 	}
332 
333 	DPRINTF(rcs, DAPI, (CE_CONT, "pending request=%d, req type=%x\n",
334 	    dps->pending_request, request->msg_type));
335 
336 	rmc_comm_wait_enable_to_send(rcs, dps);
337 
338 	/*
339 	 * We now have control of the RMC.
340 	 * Place a lower limit on the shortest amount of time to be
341 	 * waited before timing out while communicating with the RMC
342 	 */
343 	if (wait_time < DP_MIN_TIMEOUT)
344 		wait_time = DP_MIN_TIMEOUT;
345 
346 	stop_clockt = ddi_get_lbolt() + drv_usectohz(wait_time * 1000);
347 
348 	/*
349 	 * initialization of the request/response data structure
350 	 */
351 	drr->flags = 0;
352 	drr->error_status = 0;
353 
354 	/*
355 	 * set the 'expected reply' buffer: get the buffer already allocated
356 	 * for the response (if a reply is expected!)
357 	 */
358 	if (response != NULL) {
359 		exp_resp->msg_type = response->msg_type;
360 		exp_resp->msg_buf = (uint8_t *)response->msg_buf;
361 		exp_resp->msg_msglen = (uint16_t)response->msg_bytes;
362 		exp_resp->msg_bufsiz = (uint16_t)response->msg_len;
363 	} else {
364 		exp_resp->msg_type = DP_NULL_MSG;
365 		exp_resp->msg_buf = (uint8_t)NULL;
366 		exp_resp->msg_bufsiz = (uint16_t)0;
367 		exp_resp->msg_msglen = (uint16_t)0;
368 	}
369 
370 	/*
371 	 * send the request message
372 	 */
373 	req.msg_type = request->msg_type;
374 	req.msg_buf = (uint8_t *)request->msg_buf;
375 	req.msg_msglen = (uint16_t)request->msg_len;
376 
377 	/*
378 	 * send the message and wait for the reply or ACKnowledgment
379 	 * re-send the message if reply/ACK is not received in the
380 	 * timeframe defined
381 	 */
382 	DPRINTF(rcs, DAPI, (CE_CONT, "send request=%x\n", request->msg_type));
383 
384 	while ((err = rmc_comm_dp_msend(rcs, &req)) == RCNOERR) {
385 
386 		resend_clockt = ddi_get_lbolt() +
387 		    drv_usectohz(TX_RETRY_TIME * 1000);
388 
389 		/*
390 		 * wait for a reply or an acknowledgement
391 		 */
392 		(void) cv_timedwait(drr->cv_wait_reply, dps->dp_mutex,
393 		    resend_clockt);
394 
395 		DPRINTF(rcs, DAPI, (CE_CONT,
396 		    "reqresp send status: flags=%02x req=%x resp=%x tick=%ld\n",
397 		    drr->flags, request->msg_type,
398 		    response ? response->msg_type : -1,
399 		    stop_clockt - resend_clockt));
400 
401 		/*
402 		 * Check for error condition first
403 		 * Then, check if the command has been replied/ACKed
404 		 * Then, check if it has timeout and if there is any
405 		 * time left to resend the message.
406 		 */
407 		if ((drr->flags & MSG_ERROR) != 0) {
408 			if (drr->error_status == 0) {
409 				err = RCEGENERIC;
410 			} else {
411 				err = drr->error_status;
412 			}
413 			break;
414 
415 		} else if (response != NULL &&
416 		    (drr->flags & MSG_REPLY_RXED) != 0) {
417 			/*
418 			 * yes! here is the reply
419 			 */
420 
421 			/*
422 			 * get the actual length of the msg
423 			 * a negative value means that the reply message
424 			 * was too big for the receiver buffer
425 			 */
426 			response->msg_bytes = exp_resp->msg_msglen;
427 			if (response->msg_bytes < 0)
428 				err = RCEREPTOOBIG;
429 			else
430 				err = RCNOERR;
431 			break;
432 
433 		} else if (response == NULL && (drr->flags & MSG_ACKED) != 0) {
434 			/*
435 			 * yes! message has been acknowledged
436 			 */
437 
438 			err = RCNOERR;
439 			break;
440 
441 		} else if ((stop_clockt - resend_clockt) <= 0) {
442 			/*
443 			 * no more time left. set the error code,
444 			 * exit the loop
445 			 */
446 
447 			err = RCETIMEOUT;
448 			break;
449 		}
450 	}
451 
452 	rmc_comm_dp_mcleanup(rcs);
453 
454 	rmc_comm_wake_up_next(rcs);
455 
456 	mutex_exit(dps->dp_mutex);
457 
458 	DPRINTF(rcs, DAPI, (CE_CONT, "reqresp end: err=%d, request=%x\n",
459 	    err, request->msg_type));
460 
461 	return (err);
462 }
463 
464 /*
465  * Function used to send a BP (Boot Prom) message and get the reply.
466  * BP protocol is provided only to support firmware download.
467  *
468  * This function will look for the following key BP protocol commands:
469  * BP_OBP_BOOTINIT: the data link is brought down so that request/response
470  * sessions cannot be started. The reason why is that this command will cause
471  * RMC fw to jump to the boot monitor (BOOTMON_FLASH) and data protocol is not
472  * operational. In this context, RMC fw will only be using the BP protocol.
473  * BP_OBP_RESET: data link setup timer is resumed. This command cause the RMC
474  * to reboot and hence become operational.
475  */
476 int
477 rmc_comm_request_response_bp(rmc_comm_msg_t *request_bp,
478     rmc_comm_msg_t *response_bp, uint32_t wait_time)
479 {
480 	struct rmc_comm_state	*rcs;
481 	rmc_comm_dp_state_t	*dps;
482 	dp_req_resp_t		*drr;
483 	dp_message_t		*resp_bp;
484 	bp_msg_t		*bp_msg;
485 	clock_t			stop_clockt;
486 	int			err = RCNOERR;
487 	boolean_t		bootinit_sent = 0;
488 
489 	/*
490 	 * get the soft state struct (instance 0)
491 	 */
492 	if ((rcs = rmc_comm_getstate(NULL, 0,
493 				"rmc_comm_request_response_bp")) == NULL)
494 		return (RCENOSOFTSTATE);
495 
496 	/*
497 	 * sanity check: request_bp buffer must always be provided
498 	 */
499 	if (request_bp == NULL) {
500 		DPRINTF(rcs, DAPI, (CE_CONT, "reqresp_bp, invalid args\n"));
501 		return (RCEINVARG);
502 	}
503 
504 	bp_msg = (bp_msg_t *)request_bp->msg_buf;
505 
506 	DPRINTF(rcs, DAPI, (CE_CONT, "send request_bp=%x\n", bp_msg->cmd));
507 
508 	/*
509 	 * only BP message can be sent
510 	 */
511 	if (!IS_BOOT_MSG(bp_msg->cmd)) {
512 		DPRINTF(rcs, DAPI, (CE_CONT,
513 		    "reqresp_bp, only BP msg are allowed! type=%x\n",
514 		    bp_msg->cmd));
515 		return (RCEINVARG);
516 	}
517 
518 	dps = &rcs->dp_state;
519 	drr = &dps->req_resp;
520 	resp_bp = &drr->response;
521 
522 	mutex_enter(dps->dp_mutex);
523 
524 	rmc_comm_wait_enable_to_send(rcs, dps);
525 
526 	/*
527 	 * Now, before sending the message, just check what it is being sent
528 	 * and take action accordingly.
529 	 *
530 	 * is it BP_OBP_BOOTINIT or BP_OBP_RESET command?
531 	 */
532 	if (bp_msg->cmd == BP_OBP_BOOTINIT) {
533 
534 		/*
535 		 * bring down the protocol data link
536 		 * (must be done before aborting a request/response session)
537 		 */
538 		dps->data_link_ok = 0;
539 		dps->timer_link_setup = (timeout_id_t)0;
540 
541 		bootinit_sent = 1;
542 
543 	} else if (bp_msg->cmd == BP_OBP_RESET) {
544 
545 		/*
546 		 * restart the data link set up timer. RMC is coming up...
547 		 */
548 
549 		dp_reset(rcs, INITIAL_SEQID, 0, 1);
550 	}
551 
552 	/*
553 	 * initialization of the request/response data structure
554 	 */
555 	drr->flags = 0;
556 	drr->error_status = 0;
557 
558 	/*
559 	 * set the reply buffer: get the buffer already allocated
560 	 * for the response
561 	 */
562 	if (response_bp != NULL) {
563 		DPRINTF(rcs, DAPI, (CE_CONT, "expect BP reply. len=%d\n",
564 		    response_bp->msg_len));
565 
566 		resp_bp->msg_buf = (uint8_t *)response_bp->msg_buf;
567 		resp_bp->msg_bufsiz = (uint16_t)response_bp->msg_len;
568 	}
569 
570 	/*
571 	 * send the BP message and wait for the reply
572 	 */
573 
574 	rmc_comm_bp_msend(rcs, bp_msg);
575 
576 	if (response_bp != NULL) {
577 
578 		/*
579 		 * place a lower limit on the shortest amount of time to be
580 		 * waited before timing out while communicating with the RMC
581 		 */
582 		if (wait_time < DP_MIN_TIMEOUT)
583 			wait_time = DP_MIN_TIMEOUT;
584 
585 		stop_clockt = ddi_get_lbolt() + drv_usectohz(wait_time * 1000);
586 
587 		if ((err = rmc_comm_wait_bp_reply(rcs, dps, drr,
588 		    stop_clockt)) == RCNOERR) {
589 
590 			/*
591 			 * get the actual length of the msg
592 			 * a negative value means that the reply message
593 			 * was too big for the receiver buffer
594 			 */
595 			response_bp->msg_bytes = resp_bp->msg_msglen;
596 			if (response_bp->msg_bytes < 0) {
597 				err = RCEREPTOOBIG;
598 
599 			} else if (bootinit_sent) {
600 
601 				/*
602 				 * BOOTINIT cmd may fail. In this is the case,
603 				 * the RMC is still operational. Hence, we
604 				 * try (once) to set up the data link
605 				 * protocol.
606 				 */
607 				bp_msg = (bp_msg_t *)response_bp->msg_buf;
608 
609 				if (bp_msg->cmd == BP_RSC_BOOTFAIL &&
610 				    bp_msg->dat1 == BP_DAT1_REJECTED) {
611 					(void) rmc_comm_dp_ctlsend(rcs,
612 					    DP_CTL_START);
613 				}
614 			}
615 		}
616 	}
617 
618 	rmc_comm_dp_mcleanup(rcs);
619 
620 	rmc_comm_wake_up_next(rcs);
621 
622 	mutex_exit(dps->dp_mutex);
623 
624 	return (err);
625 }
626 
627 
628 /*
629  * to register for an asynchronous (via soft interrupt) notification
630  * of a message from the remote side (RMC)
631  */
632 int
633 rmc_comm_reg_intr(uint8_t msg_type, rmc_comm_intrfunc_t intr_handler,
634     rmc_comm_msg_t *msgbuf, uint_t *state, kmutex_t *lock)
635 {
636 	struct rmc_comm_state 	*rcs;
637 	dp_msg_intr_t		*msgintr;
638 	int			 err = RCNOERR;
639 
640 	if ((rcs = rmc_comm_getstate(NULL, 0, "rmc_comm_reg_intr")) == NULL)
641 		return (RCENOSOFTSTATE);
642 
643 	mutex_enter(rcs->dp_state.dp_mutex);
644 
645 	msgintr = &rcs->dp_state.msg_intr;
646 
647 	/*
648 	 * lock is required. If it is not defined, the
649 	 * interrupt handler routine cannot be registered.
650 	 */
651 	if (lock == NULL) {
652 		mutex_exit(rcs->dp_state.dp_mutex);
653 		return (RCEINVARG);
654 	}
655 
656 	/*
657 	 * only one interrupt handler can be registered.
658 	 */
659 	if (msgintr->intr_handler == NULL) {
660 
661 		if (ddi_add_softintr(rcs->dip, DDI_SOFTINT_HIGH,
662 		    &msgintr->intr_id, NULL, NULL, intr_handler,
663 		    (caddr_t)msgbuf) == DDI_SUCCESS) {
664 
665 			msgintr->intr_handler = intr_handler;
666 			msgintr->intr_lock = lock;
667 			msgintr->intr_state = state;
668 			msgintr->intr_msg_type = msg_type;
669 			msgintr->intr_arg = (caddr_t)msgbuf;
670 		} else {
671 			err = RCECANTREGINTR;
672 		}
673 	} else {
674 		err = RCEALREADYREG;
675 	}
676 
677 	mutex_exit(rcs->dp_state.dp_mutex);
678 
679 	return (err);
680 }
681 
682 /*
683  * To unregister for asynchronous notifications
684  */
685 int
686 rmc_comm_unreg_intr(uint8_t msg_type, rmc_comm_intrfunc_t intr_handler)
687 {
688 	struct rmc_comm_state	*rcs;
689 	dp_msg_intr_t		*msgintr;
690 	int			 err = RCNOERR;
691 
692 	if ((rcs = rmc_comm_getstate(NULL, 0, "rmc_comm_unreg_intr")) == NULL)
693 		return (RCENOSOFTSTATE);
694 
695 	mutex_enter(rcs->dp_state.dp_mutex);
696 
697 	msgintr = &rcs->dp_state.msg_intr;
698 
699 	if (msgintr->intr_handler != NULL &&
700 		msgintr->intr_msg_type == msg_type &&
701 		msgintr->intr_handler == intr_handler) {
702 
703 		ddi_remove_softintr(msgintr->intr_id);
704 		msgintr->intr_handler = NULL;
705 		msgintr->intr_id = 0;
706 		msgintr->intr_msg_type = 0;
707 		msgintr->intr_arg = NULL;
708 		msgintr->intr_lock = NULL;
709 		msgintr->intr_state = NULL;
710 	} else {
711 		err = RCEGENERIC;
712 	}
713 
714 	mutex_exit(rcs->dp_state.dp_mutex);
715 
716 	return (err);
717 }
718 
719 /*
720  * To send raw data (firmware s-records) down to the RMC.
721  * It is provided only to support firmware download.
722  */
723 int
724 rmc_comm_send_srecord_bp(caddr_t buf, int buflen,
725     rmc_comm_msg_t *response_bp, uint32_t wait_time)
726 {
727 	struct rmc_comm_state	*rcs;
728 	rmc_comm_dp_state_t	*dps;
729 	dp_req_resp_t		*drr;
730 	dp_message_t		*resp_bp;
731 	clock_t			stop_clockt;
732 	int			err;
733 
734 	/*
735 	 * get the soft state struct (instance 0)
736 	 */
737 	if ((rcs = rmc_comm_getstate(NULL, 0,
738 				"rmc_comm_request_response_bp")) == NULL)
739 		return (RCENOSOFTSTATE);
740 
741 	/*
742 	 * sanity check: response_bp buffer must always be provided
743 	 */
744 	if (buf == NULL || response_bp == NULL) {
745 		DPRINTF(rcs, DAPI, (CE_CONT, "send_srecord_bp,invalid args\n"));
746 		return (RCEINVARG);
747 	}
748 
749 	DPRINTF(rcs, DAPI, (CE_CONT, "send_srecord_bp, buflen=%d\n", buflen));
750 
751 	dps = &rcs->dp_state;
752 	drr = &dps->req_resp;
753 	resp_bp = &drr->response;
754 
755 	mutex_enter(dps->dp_mutex);
756 
757 	rmc_comm_wait_enable_to_send(rcs, dps);
758 
759 	/*
760 	 * initialization of the request/response data structure
761 	 */
762 	drr->flags = 0;
763 	drr->error_status = 0;
764 
765 	/*
766 	 * set the reply buffer: get the buffer already allocated
767 	 * for the response
768 	 */
769 	resp_bp->msg_buf = (uint8_t *)response_bp->msg_buf;
770 	resp_bp->msg_bufsiz = (uint16_t)response_bp->msg_len;
771 
772 	/*
773 	 * send raw data (s-record) and wait for the reply (BP message)
774 	 */
775 
776 	rmc_comm_bp_srecsend(rcs, (char *)buf, buflen);
777 
778 	/*
779 	 * place a lower limit on the shortest amount of time to be
780 	 * waited before timing out while communicating with the RMC
781 	 */
782 	if (wait_time < DP_MIN_TIMEOUT)
783 		wait_time = DP_MIN_TIMEOUT;
784 
785 	stop_clockt = ddi_get_lbolt() + drv_usectohz(wait_time * 1000);
786 
787 	if ((err = rmc_comm_wait_bp_reply(rcs, dps, drr,
788 	    stop_clockt)) == RCNOERR) {
789 		/*
790 		 * get the actual length of the msg
791 		 * a negative value means that the reply message
792 		 * was too big for the receiver buffer
793 		 */
794 		response_bp->msg_bytes = resp_bp->msg_msglen;
795 		if (response_bp->msg_bytes < 0) {
796 			err = RCEREPTOOBIG;
797 		}
798 	}
799 
800 	rmc_comm_dp_mcleanup(rcs);
801 
802 	rmc_comm_wake_up_next(rcs);
803 
804 	mutex_exit(dps->dp_mutex);
805 
806 	return (err);
807 }
808 
809 /*
810  * To wait for (any) BP message to be received.
811  * (dp_mutex must be held)
812  */
813 static int
814 rmc_comm_wait_bp_reply(struct rmc_comm_state *rcs, rmc_comm_dp_state_t *dps,
815     dp_req_resp_t *drr, clock_t stop_clockt)
816 {
817 	clock_t clockleft = 1;
818 	int err = RCNOERR;
819 
820 	clockleft = cv_timedwait(drr->cv_wait_reply, dps->dp_mutex,
821 	    stop_clockt);
822 
823 
824 	DPRINTF(rcs, DAPI, (CE_CONT,
825 	    "reqresp_bp, send: flags=%02x, clktick left=%ld\n",
826 	    drr->flags, clockleft));
827 
828 	/*
829 	 * Check for error condition first.
830 	 * Then, check if it has timeout.
831 	 * Then, check if the command has been replied.
832 	 */
833 	if ((drr->flags & MSG_ERROR) != 0) {
834 
835 		err = RCEGENERIC;
836 
837 	} else if (clockleft <= 0) {
838 		/*
839 		 * timeout
840 		 */
841 
842 		err = RCETIMEOUT;
843 
844 	} else if ((drr->flags & MSG_RXED_BP) == 0) {
845 
846 		err = RCEGENERIC;
847 	}
848 
849 	return (err);
850 }
851 
852 /*
853  * Wait for the pending_request flag to be cleared and acquire it for our
854  * own use. The caller is then allowed to start a new request/response
855  * session with the RMC.
856  * Note that all send-receive actions to the RMC include a time-out, so
857  * the pending-request must eventually go away - even if the RMC is down.
858  * Hence there is no need to timeout the wait action of this function.
859  * (dp_mutex must be held on entry).
860  */
861 static void
862 rmc_comm_wait_enable_to_send(struct rmc_comm_state *rcs,
863     rmc_comm_dp_state_t *dps)
864 {
865 	DPRINTF(rcs, DAPI, (CE_CONT, "pending request=%d\n",
866 	    dps->pending_request));
867 
868 	/*
869 	 * A new message can actually grab the lock before the thread
870 	 * that has just been signaled.  Therefore, we need to double
871 	 * check to make sure that pending_request is not already set
872 	 * after we wake up.
873 	 *
874 	 * Potentially this could mean starvation for certain unfortunate
875 	 * threads that keep getting woken up and putting back to sleep.
876 	 * But the window of such contention is very small to begin with.
877 	 */
878 
879 	while (dps->pending_request) {
880 		/*
881 		 * just 'sit and wait' until there are no pending requests
882 		 */
883 
884 		cv_wait(dps->cv_ok_to_send, dps->dp_mutex);
885 	}
886 
887 	/*
888 	 * now a request/response can be started. Set the flag so that nobody
889 	 * else will be able to send anything.
890 	 */
891 	dps->pending_request = 1;
892 }
893 
894 /*
895  * To wake up one of the threads (if any) waiting for starting a
896  * request/response session.
897  * (dp_mutex must be held)
898  */
899 static void
900 rmc_comm_wake_up_next(struct rmc_comm_state *rcs)
901 {
902 	/*
903 	 * wake up eventual waiting threads...
904 	 */
905 
906 	rcs->dp_state.pending_request = 0;
907 	cv_signal(rcs->dp_state.cv_ok_to_send);
908 }
909 
910 
911 /*
912  * thread which delivers pending request message to the rmc. Some leaf drivers
913  * cannot afford to wait for a request to be replied/ACKed. Hence, a request
914  * message is stored temporarily in the state structure and this thread
915  * gets woken up to deliver it.
916  */
917 static void
918 rmc_comm_send_pend_req(caddr_t arg)
919 {
920 	struct rmc_comm_state		*rcs;
921 	rmc_comm_drvintf_state_t	*dis;
922 	callb_cpr_t			cprinfo;
923 
924 	if (arg == NULL) {
925 		thread_exit();
926 		/* NOTREACHED */
927 	}
928 
929 	rcs = (struct rmc_comm_state *)arg;
930 	dis = &rcs->drvi_state;
931 
932 	CALLB_CPR_INIT(&cprinfo, dis->dreq_mutex, callb_generic_cpr,
933 	    "rmc_comm_send_pend_req");
934 
935 	mutex_enter(dis->dreq_mutex);
936 
937 	if (dis->dreq_state <= RMC_COMM_DREQ_ST_READY)
938 		dis->dreq_state = RMC_COMM_DREQ_ST_WAIT;
939 
940 	for (;;) {
941 
942 		/*
943 		 * Wait for someone to tell me to continue.
944 		 */
945 		while (dis->dreq_state == RMC_COMM_DREQ_ST_WAIT) {
946 			CALLB_CPR_SAFE_BEGIN(&cprinfo);
947 			cv_wait(dis->dreq_sig_cv, dis->dreq_mutex);
948 			CALLB_CPR_SAFE_END(&cprinfo, dis->dreq_mutex);
949 		}
950 
951 		/* RMC_COMM_DREQ_ST_EXIT implies signal by _detach(). */
952 		if (dis->dreq_state == RMC_COMM_DREQ_ST_EXIT) {
953 			dis->dreq_state = RMC_COMM_DREQ_ST_NOTSTARTED;
954 			dis->dreq_tid = 0;
955 
956 			/* dis->dreq_mutex is held at this point! */
957 			CALLB_CPR_EXIT(&cprinfo);
958 
959 			thread_exit();
960 			/* NOTREACHED */
961 		}
962 
963 		ASSERT(dis->dreq_state == RMC_COMM_DREQ_ST_PROCESS);
964 		mutex_exit(dis->dreq_mutex);
965 
966 		/*
967 		 * deliver the request (and wait...)
968 		 */
969 		(void) rmc_comm_send_req_resp(rcs, &dis->dreq_request, NULL,
970 		    RMC_COMM_DREQ_DEFAULT_TIME);
971 
972 		mutex_enter(dis->dreq_mutex);
973 		if (dis->dreq_state != RMC_COMM_DREQ_ST_EXIT)
974 			dis->dreq_state = RMC_COMM_DREQ_ST_WAIT;
975 	}
976 }
977 
978 /*
979  * start thread to deal with pending requests to be delivered asynchronously
980  * (i.e. leaf driver do not have to/cannot wait for a reply/ACk of a request)
981  */
982 static int
983 rmc_comm_dreq_thread_start(struct rmc_comm_state *rcs)
984 {
985 	rmc_comm_drvintf_state_t *dis = &rcs->drvi_state;
986 	int err = 0;
987 	kthread_t *tp;
988 
989 	mutex_enter(dis->dreq_mutex);
990 
991 	if (dis->dreq_state == RMC_COMM_DREQ_ST_NOTSTARTED) {
992 
993 		tp = thread_create(NULL, 0, rmc_comm_send_pend_req,
994 		    (caddr_t)rcs, 0, &p0, TS_RUN, maxclsyspri);
995 		dis->dreq_state = RMC_COMM_DREQ_ST_READY;
996 		dis->dreq_tid = tp->t_did;
997 	}
998 
999 	mutex_exit(dis->dreq_mutex);
1000 
1001 	return (err);
1002 }
1003 
1004 /*
1005  * stop the thread (to deliver pending request messages)
1006  */
1007 static void
1008 rmc_comm_dreq_thread_kill(struct rmc_comm_state *rcs)
1009 {
1010 	rmc_comm_drvintf_state_t *dis = &rcs->drvi_state;
1011 	kt_did_t tid;
1012 
1013 	mutex_enter(dis->dreq_mutex);
1014 	tid = dis->dreq_tid;
1015 	if (tid != 0) {
1016 		dis->dreq_state = RMC_COMM_DREQ_ST_EXIT;
1017 		dis->dreq_tid = 0;
1018 		cv_signal(dis->dreq_sig_cv);
1019 	}
1020 	mutex_exit(dis->dreq_mutex);
1021 
1022 	/*
1023 	 * Wait for rmc_comm_send_pend_req() to finish
1024 	 */
1025 	if (tid != 0)
1026 		thread_join(tid);
1027 }
1028 
1029 /*
1030  * init function - start thread to deal with pending requests (no-wait requests)
1031  */
1032 int
1033 rmc_comm_drvintf_init(struct rmc_comm_state *rcs)
1034 {
1035 	int err = 0;
1036 
1037 	DPRINTF(rcs, DGEN, (CE_CONT, "rmc_comm_drvintf_init\n"));
1038 	rcs->drvi_state.dreq_state = RMC_COMM_DREQ_ST_NOTSTARTED;
1039 	rcs->drvi_state.dreq_tid = 0;
1040 
1041 	mutex_init(rcs->drvi_state.dreq_mutex, NULL, MUTEX_DRIVER, NULL);
1042 	cv_init(rcs->drvi_state.dreq_sig_cv, NULL, CV_DRIVER, NULL);
1043 
1044 	err = rmc_comm_dreq_thread_start(rcs);
1045 	if (err != 0) {
1046 		cv_destroy(rcs->drvi_state.dreq_sig_cv);
1047 		mutex_destroy(rcs->drvi_state.dreq_mutex);
1048 	}
1049 
1050 	DPRINTF(rcs, DGEN, (CE_CONT, "thread started? err=%d\n", err));
1051 
1052 	return (err);
1053 }
1054 
1055 /*
1056  * fini function - kill thread to deal with pending requests (no-wait requests)
1057  */
1058 void
1059 rmc_comm_drvintf_fini(struct rmc_comm_state *rcs)
1060 {
1061 	DPRINTF(rcs, DGEN, (CE_CONT, "rmc_comm_drvintf_fini:stop thread\n"));
1062 
1063 	rmc_comm_dreq_thread_kill(rcs);
1064 
1065 	DPRINTF(rcs, DGEN, (CE_CONT, "rmc_comm_drvintf_fini:destroy Mx/CVs\n"));
1066 
1067 	cv_destroy(rcs->drvi_state.dreq_sig_cv);
1068 	mutex_destroy(rcs->drvi_state.dreq_mutex);
1069 }
1070