xref: /illumos-gate/usr/src/uts/sun4u/io/rmcadm.c (revision 725953abcc30bcf6f15d7b8a313ab0749275d95f)
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 2008 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 
28 #include <sys/stat.h>
29 #include <sys/conf.h>
30 #include <sys/modctl.h>
31 #include <sys/ddi.h>
32 #include <sys/rmc_comm_dp.h>
33 #include <sys/rmc_comm_dp_boot.h>
34 #include <sys/rmc_comm_drvintf.h>
35 #include <sys/cyclic.h>
36 #include <sys/rmc_comm.h>
37 #include <sys/machsystm.h>
38 #include <sys/file.h>
39 #include <sys/rmcadm.h>
40 
41 /*
42  * functions local to this driver.
43  */
44 static int	rmcadm_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg,
45     void **resultp);
46 static int	rmcadm_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
47 static int	rmcadm_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
48 static int	rmcadm_open(dev_t *dev_p, int flag, int otyp, cred_t *cred_p);
49 static int	rmcadm_close(dev_t dev, int flag, int otyp, cred_t *cred_p);
50 static int	rmcadm_ioctl(dev_t dev, int cmd, intptr_t arg, int mode,
51     cred_t *cred_p, int *rval_p);
52 
53 /*
54  * Driver entry points
55  */
56 static struct cb_ops rmcadm_cb_ops = {
57 	rmcadm_open,	/* open */
58 	rmcadm_close,	/* close */
59 	nodev,		/* strategy() */
60 	nodev,		/* print() */
61 	nodev,		/* dump() */
62 	nodev,		/* read() */
63 	nodev,		/* write() */
64 	rmcadm_ioctl,	/* ioctl() */
65 	nodev,		/* devmap() */
66 	nodev,		/* mmap() */
67 	ddi_segmap,	/* segmap() */
68 	nochpoll,	/* poll() */
69 	ddi_prop_op,    /* prop_op() */
70 	NULL,		/* cb_str */
71 	D_NEW | D_MP	/* cb_flag */
72 };
73 
74 
75 static struct dev_ops rmcadm_ops = {
76 	DEVO_REV,
77 	0,			/* ref count */
78 	rmcadm_getinfo,		/* getinfo() */
79 	nulldev,		/* identify() */
80 	nulldev,		/* probe() */
81 	rmcadm_attach,		/* attach() */
82 	rmcadm_detach,		/* detach */
83 	nodev,			/* reset */
84 	&rmcadm_cb_ops,		/* pointer to cb_ops structure */
85 	(struct bus_ops *)NULL,
86 	nulldev,		/* power() */
87 	ddi_quiesce_not_needed,		/* quiesce */
88 };
89 
90 /*
91  * Loadable module support.
92  */
93 extern struct mod_ops mod_driverops;
94 
95 static struct modldrv modldrv = {
96 	&mod_driverops,			/* Type of module. This is a driver */
97 	"rmcadm control driver",	/* Name of the module */
98 	&rmcadm_ops			/* pointer to the dev_ops structure */
99 };
100 
101 static struct modlinkage modlinkage = {
102 	MODREV_1,
103 	&modldrv,
104 	NULL
105 };
106 
107 static dev_info_t		*rmcadm_dip = NULL;
108 
109 extern void pmugpio_reset();
110 
111 /*
112  * Utilities...
113  */
114 
115 /*
116  * to return the errno from the rmc_comm error status
117  */
118 int
119 rmcadm_get_errno(int status)
120 {
121 	int retval = EIO;
122 
123 	/* errors from RMC */
124 	switch (status) {
125 		case RCENOSOFTSTATE:
126 			/* invalid/NULL soft state structure */
127 			retval = EIO;
128 			break;
129 		case RCENODATALINK:
130 			/* data protocol not available (down) */
131 			retval = EIO;
132 			break;
133 		case RCENOMEM:
134 			/* memory problems */
135 			retval = ENOMEM;
136 			break;
137 		case RCECANTRESEND:
138 			/* resend failed */
139 			retval = EIO;
140 			break;
141 		case RCEMAXRETRIES:
142 			/* reply not received - retries exceeded */
143 			retval = EINTR;
144 			break;
145 		case RCETIMEOUT:
146 			/* reply not received - command has timed out */
147 			retval = EINTR;
148 			break;
149 		case RCEINVCMD:
150 			/* data protocol cmd not supported */
151 			retval = ENOTSUP;
152 			break;
153 		case RCEINVARG:
154 			/* invalid argument(s) */
155 			retval = ENOTSUP;
156 			break;
157 		case RCEGENERIC:
158 			/* generic error */
159 			retval = EIO;
160 			break;
161 		default:
162 			retval = EIO;
163 			break;
164 	}
165 	return (retval);
166 }
167 
168 int
169 _init(void)
170 {
171 	int	error = 0;
172 
173 	error = mod_install(&modlinkage);
174 	return (error);
175 }
176 
177 
178 int
179 _info(struct modinfo *modinfop)
180 {
181 	return (mod_info(&modlinkage, modinfop));
182 }
183 
184 
185 int
186 _fini(void)
187 {
188 	int	error = 0;
189 
190 	error = mod_remove(&modlinkage);
191 	if (error)
192 		return (error);
193 	return (error);
194 }
195 
196 
197 /* ARGSUSED */
198 static int
199 rmcadm_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **resultp)
200 {
201 	minor_t m = getminor((dev_t)arg);
202 
203 	switch (cmd) {
204 	case DDI_INFO_DEVT2DEVINFO:
205 		if ((m != 0) || (rmcadm_dip == NULL)) {
206 			*resultp = NULL;
207 			return (DDI_FAILURE);
208 		}
209 		*resultp = rmcadm_dip;
210 		return (DDI_SUCCESS);
211 	case DDI_INFO_DEVT2INSTANCE:
212 		*resultp = (void *)(uintptr_t)m;
213 		return (DDI_SUCCESS);
214 	default:
215 		return (DDI_FAILURE);
216 	}
217 }
218 
219 
220 static int
221 rmcadm_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
222 {
223 	int			instance;
224 	int			err;
225 
226 	switch (cmd) {
227 	case DDI_ATTACH:
228 		/*
229 		 * only allow one instance
230 		 */
231 		instance = ddi_get_instance(dip);
232 		if (instance != 0)
233 			return (DDI_FAILURE);
234 
235 		err = ddi_create_minor_node(dip, "rmcadm", S_IFCHR,
236 		    instance, DDI_PSEUDO, 0);
237 		if (err != DDI_SUCCESS)
238 			return (DDI_FAILURE);
239 
240 		/*
241 		 * Register with rmc_comm to prevent it being detached
242 		 */
243 		err = rmc_comm_register();
244 		if (err != DDI_SUCCESS) {
245 			ddi_remove_minor_node(dip, NULL);
246 			return (DDI_FAILURE);
247 		}
248 
249 		/* Remember the dev info */
250 		rmcadm_dip = dip;
251 
252 		ddi_report_dev(dip);
253 		return (DDI_SUCCESS);
254 	case DDI_RESUME:
255 		return (DDI_SUCCESS);
256 	default:
257 		return (DDI_FAILURE);
258 	}
259 }
260 
261 
262 static int
263 rmcadm_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
264 {
265 	int	instance;
266 
267 	switch (cmd) {
268 	case DDI_DETACH:
269 		instance = ddi_get_instance(dip);
270 		if (instance != 0)
271 			return (DDI_FAILURE);
272 
273 		rmcadm_dip = NULL;
274 		ddi_remove_minor_node(dip, NULL);
275 		rmc_comm_unregister();
276 		return (DDI_SUCCESS);
277 	case DDI_SUSPEND:
278 		return (DDI_SUCCESS);
279 	default:
280 		return (DDI_FAILURE);
281 	}
282 }
283 
284 /*ARGSUSED*/
285 static int
286 rmcadm_open(dev_t *dev_p, int flag, int otyp, cred_t *cred_p)
287 {
288 	int error = 0;
289 	int instance = getminor(*dev_p);
290 
291 	if (instance != 0)
292 		return (ENXIO);
293 
294 	if ((error = drv_priv(cred_p)) != 0) {
295 		cmn_err(CE_WARN, "rmcadm: inst %d drv_priv failed",
296 		    instance);
297 		return (error);
298 	}
299 	return (error);
300 }
301 
302 /*ARGSUSED*/
303 static int
304 rmcadm_close(dev_t dev, int flag, int otyp, cred_t *cred_p)
305 {
306 	return (DDI_SUCCESS);
307 }
308 
309 /*ARGSUSED*/
310 static int
311 rmcadm_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cred_p,
312     int *rval_p)
313 {
314 	int				instance = getminor(dev);
315 	int				retval = 0;
316 	rmcadm_request_response_t	rr;
317 	rmcadm_send_srecord_bp_t	ssbp;
318 	rmc_comm_msg_t			rmc_req, *rmc_reqp = &rmc_req;
319 	rmc_comm_msg_t			rmc_resp, *rmc_respp = &rmc_resp;
320 	caddr_t				user_req_buf;
321 	caddr_t				user_data_buf;
322 	caddr_t				user_resp_buf;
323 
324 	if (instance != 0)
325 		return (ENXIO);
326 
327 	switch (cmd) {
328 
329 	case RMCADM_REQUEST_RESPONSE:
330 	case RMCADM_REQUEST_RESPONSE_BP:
331 
332 		/*
333 		 * first copy in the request_response structure
334 		 */
335 #ifdef _MULTI_DATAMODEL
336 		switch (ddi_model_convert_from(mode & FMODELS)) {
337 		case DDI_MODEL_ILP32:
338 		{
339 			/*
340 			 * For use when a 32 bit app makes a call into a
341 			 * 64 bit ioctl
342 			 */
343 			rmcadm_request_response32_t	rr32;
344 
345 			if (ddi_copyin((caddr_t)arg, (caddr_t)&rr32,
346 			    sizeof (rr32), mode)) {
347 				return (EFAULT);
348 			}
349 			rr.req.msg_type = rr32.req.msg_type;
350 			rr.req.msg_len = rr32.req.msg_len;
351 			rr.req.msg_bytes = rr32.req.msg_bytes;
352 			rr.req.msg_buf = (caddr_t)(uintptr_t)rr32.req.msg_buf;
353 			rr.resp.msg_type = rr32.resp.msg_type;
354 			rr.resp.msg_len = rr32.resp.msg_len;
355 			rr.resp.msg_bytes = rr32.resp.msg_bytes;
356 			rr.resp.msg_buf = (caddr_t)(uintptr_t)rr32.resp.msg_buf;
357 			rr.wait_time = rr32.wait_time;
358 			break;
359 		}
360 		case DDI_MODEL_NONE:
361 			if (ddi_copyin((caddr_t)arg, (caddr_t)&rr,
362 			    sizeof (rr), mode)) {
363 				return (EFAULT);
364 			}
365 			break;
366 		}
367 #else /* ! _MULTI_DATAMODEL */
368 		if (ddi_copyin((caddr_t)arg, (caddr_t)&rr,
369 		    sizeof (rr), mode) != 0) {
370 			return (EFAULT);
371 		}
372 #endif /* _MULTI_DATAMODEL */
373 
374 		/*
375 		 * save the user request buffer pointer
376 		 */
377 		user_req_buf = rr.req.msg_buf;
378 
379 		if (user_req_buf != NULL) {
380 			/*
381 			 * copy in the request data
382 			 */
383 			rr.req.msg_buf = kmem_alloc(rr.req.msg_len, KM_SLEEP);
384 
385 			if (ddi_copyin(user_req_buf, rr.req.msg_buf,
386 			    rr.req.msg_len, mode) != 0) {
387 
388 				kmem_free(rr.req.msg_buf, rr.req.msg_len);
389 				rr.req.msg_buf = user_req_buf;
390 				return (EFAULT);
391 			}
392 		} else {
393 			if (rr.req.msg_len > 0)
394 				/*
395 				 * msg_len should be 0 if buffer is NULL!
396 				 */
397 				return (EINVAL);
398 		}
399 
400 		/*
401 		 * save the user request buffer pointer
402 		 */
403 		user_resp_buf = rr.resp.msg_buf;
404 		if (user_resp_buf != NULL) {
405 			rr.resp.msg_buf = kmem_alloc(rr.resp.msg_len, KM_SLEEP);
406 		}
407 
408 		/*
409 		 * send the request (or BP request) via the rmc_comm driver
410 		 */
411 		rmc_reqp->msg_type = rr.req.msg_type;
412 		rmc_reqp->msg_buf = rr.req.msg_buf;
413 		rmc_reqp->msg_len = rr.req.msg_len;
414 		rmc_reqp->msg_bytes = rr.req.msg_bytes;
415 
416 		if (cmd == RMCADM_REQUEST_RESPONSE) {
417 
418 			/*
419 			 * check if response is expected. If so, fill in
420 			 * the response data structure
421 			 */
422 			if (rr.resp.msg_type != DP_NULL_MSG) {
423 
424 				rmc_respp->msg_type = rr.resp.msg_type;
425 				rmc_respp->msg_buf = rr.resp.msg_buf;
426 				rmc_respp->msg_len = rr.resp.msg_len;
427 				rmc_respp->msg_bytes = rr.resp.msg_bytes;
428 
429 			} else {
430 
431 				rmc_respp = (rmc_comm_msg_t *)NULL;
432 			}
433 
434 			rr.status = rmc_comm_request_response(
435 			    rmc_reqp, rmc_respp, rr.wait_time);
436 
437 		} else { /* RMCADM_REQUEST_RESPONSE_BP */
438 
439 			/*
440 			 * check if a BP message is expected back. If so,
441 			 * fill in the response data structure
442 			 */
443 			if (rr.resp.msg_buf != NULL) {
444 
445 				rmc_respp->msg_type = rr.resp.msg_type;
446 				rmc_respp->msg_buf = rr.resp.msg_buf;
447 				rmc_respp->msg_len = rr.resp.msg_len;
448 				rmc_respp->msg_bytes = rr.resp.msg_bytes;
449 
450 			} else {
451 
452 				rmc_respp = (rmc_comm_msg_t *)NULL;
453 			}
454 
455 			rr.status = rmc_comm_request_response_bp(
456 			    rmc_reqp, rmc_respp, rr.wait_time);
457 		}
458 
459 		/*
460 		 * if a response was expected, copy back the (actual) number
461 		 * of bytes of the response returned by the
462 		 * rmc_comm_request_response function (msg_bytes field)
463 		 */
464 		if (rmc_respp != NULL) {
465 			rr.resp.msg_bytes = rmc_respp->msg_bytes;
466 		}
467 
468 		if (rr.status != RCNOERR) {
469 
470 			retval = rmcadm_get_errno(rr.status);
471 
472 		} else if (user_resp_buf != NULL) {
473 			/*
474 			 * copy out the user response buffer
475 			 */
476 			if (ddi_copyout(rr.resp.msg_buf, user_resp_buf,
477 			    rr.resp.msg_bytes, mode) != 0) {
478 				retval = EFAULT;
479 			}
480 		}
481 
482 		/*
483 		 * now copy out the updated request_response structure
484 		 */
485 		if (rr.req.msg_buf)
486 			kmem_free(rr.req.msg_buf, rr.req.msg_len);
487 		if (rr.resp.msg_buf)
488 			kmem_free(rr.resp.msg_buf, rr.resp.msg_len);
489 
490 		rr.req.msg_buf = user_req_buf;
491 		rr.resp.msg_buf = user_resp_buf;
492 
493 #ifdef _MULTI_DATAMODEL
494 		switch (ddi_model_convert_from(mode & FMODELS)) {
495 		case DDI_MODEL_ILP32:
496 		{
497 			/*
498 			 * For use when a 32 bit app makes a call into a
499 			 * 64 bit ioctl
500 			 */
501 			rmcadm_request_response32_t	rr32;
502 
503 			rr32.req.msg_type = rr.req.msg_type;
504 			rr32.req.msg_len = rr.req.msg_len;
505 			rr32.req.msg_bytes = rr.req.msg_bytes;
506 			rr32.req.msg_buf = (caddr32_t)(uintptr_t)rr.req.msg_buf;
507 			rr32.resp.msg_type = rr.resp.msg_type;
508 			rr32.resp.msg_len = rr.resp.msg_len;
509 			rr32.resp.msg_bytes = rr.resp.msg_bytes;
510 			rr32.resp.msg_buf =
511 			    (caddr32_t)(uintptr_t)rr.resp.msg_buf;
512 			rr32.wait_time = rr.wait_time;
513 			rr32.status = rr.status;
514 			if (ddi_copyout((caddr_t)&rr32, (caddr_t)arg,
515 			    sizeof (rr32), mode)) {
516 				return (EFAULT);
517 			}
518 			break;
519 		}
520 		case DDI_MODEL_NONE:
521 			if (ddi_copyout((caddr_t)&rr, (caddr_t)arg,
522 			    sizeof (rr), mode))
523 				return (EFAULT);
524 			break;
525 		}
526 #else /* ! _MULTI_DATAMODEL */
527 		if (ddi_copyout((caddr_t)&rr, (caddr_t)arg, sizeof (rr),
528 		    mode) != 0)
529 			return (EFAULT);
530 #endif /* _MULTI_DATAMODEL */
531 		break;
532 
533 
534 	case RMCADM_SEND_SRECORD_BP:
535 
536 		/*
537 		 * first copy in the request_response structure
538 		 */
539 #ifdef _MULTI_DATAMODEL
540 		switch (ddi_model_convert_from(mode & FMODELS)) {
541 		case DDI_MODEL_ILP32:
542 		{
543 			/*
544 			 * For use when a 32 bit app makes a call into a
545 			 * 64 bit ioctl
546 			 */
547 			rmcadm_send_srecord_bp32_t	ssbp32;
548 
549 			if (ddi_copyin((caddr_t)arg, (caddr_t)&ssbp32,
550 			    sizeof (ssbp32), mode)) {
551 				return (EFAULT);
552 			}
553 			ssbp.data_len = ssbp32.data_len;
554 			ssbp.data_buf = (caddr_t)(uintptr_t)ssbp32.data_buf;
555 			ssbp.resp_bp.msg_type = ssbp32.resp_bp.msg_type;
556 			ssbp.resp_bp.msg_len = ssbp32.resp_bp.msg_len;
557 			ssbp.resp_bp.msg_bytes = ssbp32.resp_bp.msg_bytes;
558 			ssbp.resp_bp.msg_buf =
559 			    (caddr_t)(uintptr_t)ssbp32.resp_bp.msg_buf;
560 			ssbp.wait_time = ssbp32.wait_time;
561 			break;
562 		}
563 		case DDI_MODEL_NONE:
564 			if (ddi_copyin((caddr_t)arg, (caddr_t)&ssbp,
565 			    sizeof (ssbp), mode))
566 				return (EFAULT);
567 			break;
568 		}
569 #else /* ! _MULTI_DATAMODEL */
570 		if (ddi_copyin((caddr_t)arg, (caddr_t)&ssbp,
571 		    sizeof (ssbp), mode) != 0)
572 			return (EFAULT);
573 #endif /* _MULTI_DATAMODEL */
574 
575 		/*
576 		 * save the user data buffer pointer
577 		 */
578 		user_data_buf = ssbp.data_buf;
579 
580 		if (user_data_buf != NULL) {
581 			/*
582 			 * copy in the srecord data
583 			 */
584 			ssbp.data_buf = kmem_alloc(ssbp.data_len, KM_SLEEP);
585 
586 			if (ddi_copyin(user_data_buf, ssbp.data_buf,
587 			    ssbp.data_len, mode) != 0) {
588 
589 				kmem_free(ssbp.data_buf, ssbp.data_len);
590 				ssbp.data_buf = user_data_buf;
591 				return (EFAULT);
592 			}
593 		} else {
594 			return (EINVAL);	/* request can't be NULL! */
595 		}
596 
597 		/*
598 		 * save the user request buffer pointer
599 		 */
600 		user_resp_buf = ssbp.resp_bp.msg_buf;
601 		if (user_resp_buf != NULL) {
602 			ssbp.resp_bp.msg_buf =
603 			    kmem_alloc(ssbp.resp_bp.msg_len, KM_SLEEP);
604 		} else {
605 
606 			kmem_free(ssbp.data_buf, ssbp.data_len);
607 			return (EINVAL);
608 		}
609 
610 		/*
611 		 * send the srecord via the rmc_comm driver and get the reply
612 		 * back (BP message)
613 		 */
614 
615 		rmc_respp->msg_type = ssbp.resp_bp.msg_type;
616 		rmc_respp->msg_buf = ssbp.resp_bp.msg_buf;
617 		rmc_respp->msg_len = ssbp.resp_bp.msg_len;
618 		rmc_respp->msg_bytes = ssbp.resp_bp.msg_bytes;
619 
620 		ssbp.status = rmc_comm_send_srecord_bp(ssbp.data_buf,
621 		    ssbp.data_len, rmc_respp, ssbp.wait_time);
622 
623 		/*
624 		 * copy back the actual size of the returned message
625 		 */
626 		ssbp.resp_bp.msg_bytes = rmc_respp->msg_bytes;
627 
628 		if (ssbp.status != RCNOERR) {
629 			retval = rmcadm_get_errno(ssbp.status);
630 
631 		} else if (user_resp_buf != NULL) {
632 			/*
633 			 * copy out the user BP response buffer
634 			 */
635 			if (ddi_copyout(ssbp.resp_bp.msg_buf, user_resp_buf,
636 			    ssbp.resp_bp.msg_bytes, mode) != 0) {
637 				retval = EFAULT;
638 			}
639 		}
640 
641 		/*
642 		 * now copy out the updated request_response structure
643 		 */
644 		if (ssbp.data_buf)
645 			kmem_free(ssbp.data_buf, ssbp.data_len);
646 		if (ssbp.resp_bp.msg_buf)
647 			kmem_free(ssbp.resp_bp.msg_buf, ssbp.resp_bp.msg_len);
648 
649 		ssbp.data_buf = user_data_buf;
650 		ssbp.resp_bp.msg_buf = user_resp_buf;
651 
652 #ifdef _MULTI_DATAMODEL
653 		switch (ddi_model_convert_from(mode & FMODELS)) {
654 		case DDI_MODEL_ILP32:
655 		{
656 			/*
657 			 * For use when a 32 bit app makes a call into a
658 			 * 64 bit ioctl
659 			 */
660 			rmcadm_send_srecord_bp32_t	ssbp32;
661 
662 			ssbp32.data_len = ssbp.data_len;
663 			ssbp32.data_buf = (caddr32_t)(uintptr_t)ssbp.data_buf;
664 			ssbp32.resp_bp.msg_type = ssbp.resp_bp.msg_type;
665 			ssbp32.resp_bp.msg_len = ssbp.resp_bp.msg_len;
666 			ssbp32.resp_bp.msg_bytes = ssbp.resp_bp.msg_bytes;
667 			ssbp32.resp_bp.msg_buf =
668 			    (caddr32_t)(uintptr_t)ssbp.resp_bp.msg_buf;
669 			ssbp32.wait_time = ssbp.wait_time;
670 
671 			if (ddi_copyout((caddr_t)&ssbp32, (caddr_t)arg,
672 			    sizeof (ssbp32), mode)) {
673 				return (EFAULT);
674 			}
675 			break;
676 		}
677 		case DDI_MODEL_NONE:
678 			if (ddi_copyout((caddr_t)&ssbp, (caddr_t)arg,
679 			    sizeof (ssbp), mode))
680 				return (EFAULT);
681 			break;
682 		}
683 #else /* ! _MULTI_DATAMODEL */
684 		if (ddi_copyout((caddr_t)&ssbp, (caddr_t)arg, sizeof (ssbp),
685 		    mode) != 0)
686 			return (EFAULT);
687 #endif /* _MULTI_DATAMODEL */
688 		break;
689 
690 
691 	case RMCADM_RESET_SP:
692 		pmugpio_reset();
693 		retval = 0;
694 		break;
695 	default:
696 		retval = ENOTSUP;
697 		break;
698 	}
699 	return (retval);
700 }
701