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