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