xref: /illumos-gate/usr/src/uts/common/io/tphci.c (revision 0bb073995ac5a95bd35f2dd790df1ea3d8c2d507)
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  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 
27 /*
28  * The tphci driver can be used to exercise the mpxio framework together
29  * with tvhci/tclient.
30  */
31 
32 #include <sys/conf.h>
33 #include <sys/file.h>
34 #include <sys/open.h>
35 #include <sys/stat.h>
36 #include <sys/modctl.h>
37 #include <sys/ddi.h>
38 #include <sys/sunddi.h>
39 #include <sys/sunndi.h>
40 #include <sys/sunmdi.h>
41 #include <sys/disp.h>
42 
43 /* cb_ops entry points */
44 static int tphci_open(dev_t *, int, int, cred_t *);
45 static int tphci_close(dev_t, int, int, cred_t *);
46 static int tphci_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
47 static int tphci_attach(dev_info_t *, ddi_attach_cmd_t);
48 static int tphci_detach(dev_info_t *, ddi_detach_cmd_t);
49 static int tphci_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
50 
51 /* bus_ops entry points */
52 static int tphci_ctl(dev_info_t *, dev_info_t *, ddi_ctl_enum_t, void *,
53     void *);
54 static int tphci_initchild(dev_info_t *, dev_info_t *);
55 static int tphci_uninitchild(dev_info_t *, dev_info_t *);
56 static int tphci_bus_config(dev_info_t *, uint_t, ddi_bus_config_op_t, void *,
57     dev_info_t **);
58 static int tphci_bus_unconfig(dev_info_t *, uint_t, ddi_bus_config_op_t,
59     void *);
60 static int tphci_intr_op(dev_info_t *dip, dev_info_t *rdip,
61     ddi_intr_op_t op, ddi_intr_handle_impl_t *hdlp, void *result);
62 
63 
64 static void *tphci_state;
65 struct tphci_state {
66 	dev_info_t *dip;
67 };
68 
69 static struct cb_ops tphci_cb_ops = {
70 	tphci_open,			/* open */
71 	tphci_close,			/* close */
72 	nodev,				/* strategy */
73 	nodev,				/* print */
74 	nodev,				/* dump */
75 	nodev,				/* read */
76 	nodev,				/* write */
77 	tphci_ioctl,			/* ioctl */
78 	nodev,				/* devmap */
79 	nodev,				/* mmap */
80 	nodev,				/* segmap */
81 	nochpoll,			/* chpoll */
82 	ddi_prop_op,			/* cb_prop_op */
83 	0,				/* streamtab */
84 	D_NEW | D_MP,			/* cb_flag */
85 	CB_REV,				/* rev */
86 	nodev,				/* aread */
87 	nodev				/* awrite */
88 };
89 
90 static struct bus_ops tphci_bus_ops = {
91 	BUSO_REV,			/* busops_rev */
92 	nullbusmap,			/* bus_map */
93 	NULL,				/* bus_get_intrspec */
94 	NULL,				/* bus_add_interspec */
95 	NULL,				/* bus_remove_interspec */
96 	i_ddi_map_fault,		/* bus_map_fault */
97 	ddi_no_dma_map,			/* bus_dma_map */
98 	ddi_no_dma_allochdl,		/* bus_dma_allochdl */
99 	NULL,				/* bus_dma_freehdl */
100 	NULL,				/* bus_dma_bindhdl */
101 	NULL,				/* bus_dma_unbindhdl */
102 	NULL,				/* bus_dma_flush */
103 	NULL,				/* bus_dma_win */
104 	NULL,				/* bus_dma_ctl */
105 	tphci_ctl,			/* bus_ctl */
106 	ddi_bus_prop_op,		/* bus_prop_op */
107 	NULL,				/* bus_get_eventcookie */
108 	NULL,				/* bus_add_eventcall */
109 	NULL,				/* bus_remove_event */
110 	NULL,				/* bus_post_event */
111 	NULL,				/* bus_intr_ctl */
112 	tphci_bus_config,		/* bus_config */
113 	tphci_bus_unconfig,		/* bus_unconfig */
114 	NULL,				/* bus_fm_init */
115 	NULL,				/* bus_fm_fini */
116 	NULL,				/* bus_fm_access_enter */
117 	NULL,				/* bus_fm_access_exit */
118 	NULL,				/* bus_power */
119 	tphci_intr_op			/* bus_intr_op */
120 };
121 
122 static struct dev_ops tphci_ops = {
123 	DEVO_REV,
124 	0,
125 	tphci_getinfo,
126 	nulldev,		/* identify */
127 	nulldev,		/* probe */
128 	tphci_attach,		/* attach and detach are mandatory */
129 	tphci_detach,
130 	nodev,			/* reset */
131 	&tphci_cb_ops,		/* cb_ops */
132 	&tphci_bus_ops,		/* bus_ops */
133 	NULL,			/* power */
134 	ddi_quiesce_not_needed,		/* quiesce */
135 };
136 
137 extern struct mod_ops mod_driverops;
138 
139 static struct modldrv modldrv = {
140 	&mod_driverops,
141 	"test phci driver",
142 	&tphci_ops
143 };
144 
145 static struct modlinkage modlinkage = {
146 	MODREV_1,
147 	&modldrv,
148 	NULL
149 };
150 
151 int
152 _init(void)
153 {
154 	int rval;
155 
156 	if ((rval = ddi_soft_state_init(&tphci_state,
157 	    sizeof (struct tphci_state), 2)) != 0) {
158 		return (rval);
159 	}
160 
161 	if ((rval = mod_install(&modlinkage)) != 0) {
162 		ddi_soft_state_fini(&tphci_state);
163 	}
164 	return (rval);
165 }
166 
167 
168 int
169 _fini(void)
170 {
171 	int rval;
172 
173 	/*
174 	 * don't start cleaning up until we know that the module remove
175 	 * has worked  -- if this works, then we know that each instance
176 	 * has successfully been detached
177 	 */
178 	if ((rval = mod_remove(&modlinkage)) != 0) {
179 		return (rval);
180 	}
181 
182 	ddi_soft_state_fini(&tphci_state);
183 
184 	return (rval);
185 }
186 
187 int
188 _info(struct modinfo *modinfop)
189 {
190 	return (mod_info(&modlinkage, modinfop));
191 }
192 
193 /* ARGSUSED */
194 static int
195 tphci_open(dev_t *devp, int flag, int otype, cred_t *credp)
196 {
197 	struct tphci_state *phci;
198 
199 	if (otype != OTYP_CHR) {
200 		return (EINVAL);
201 	}
202 
203 	phci = ddi_get_soft_state(tphci_state, getminor(*devp));
204 	if (phci == NULL) {
205 		return (ENXIO);
206 	}
207 
208 	return (0);
209 }
210 
211 
212 /* ARGSUSED */
213 static int
214 tphci_close(dev_t dev, int flag, int otype, cred_t *credp)
215 {
216 	struct tphci_state *phci;
217 	if (otype != OTYP_CHR) {
218 		return (EINVAL);
219 	}
220 
221 	phci = ddi_get_soft_state(tphci_state, getminor(dev));
222 	if (phci == NULL) {
223 		return (ENXIO);
224 	}
225 
226 	return (0);
227 }
228 
229 /* ARGSUSED */
230 static int
231 tphci_ioctl(dev_t dev, int cmd, intptr_t data, int mode,
232 	cred_t *credp, int *rval)
233 {
234 	return (0);
235 }
236 
237 /*
238  * attach the module
239  */
240 static int
241 tphci_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
242 {
243 	char *vclass;
244 	int instance, phci_regis = 0;
245 	struct tphci_state *phci = NULL;
246 
247 	instance = ddi_get_instance(dip);
248 
249 	switch (cmd) {
250 	case DDI_ATTACH:
251 		break;
252 
253 	case DDI_RESUME:
254 	case DDI_PM_RESUME:
255 		return (0);	/* nothing to do */
256 
257 	default:
258 		return (DDI_FAILURE);
259 	}
260 
261 	/*
262 	 * Allocate phci data structure.
263 	 */
264 	if (ddi_soft_state_zalloc(tphci_state, instance) != DDI_SUCCESS) {
265 		return (DDI_FAILURE);
266 	}
267 
268 	phci = ddi_get_soft_state(tphci_state, instance);
269 	ASSERT(phci != NULL);
270 	phci->dip = dip;
271 
272 	/* bus_addr has the form #,<vhci_class> */
273 	vclass = strchr(ddi_get_name_addr(dip), ',');
274 	if (vclass == NULL || vclass[1] == '\0') {
275 		cmn_err(CE_NOTE, "tphci invalid bus_addr %s",
276 		    ddi_get_name_addr(dip));
277 		goto attach_fail;
278 	}
279 
280 	/*
281 	 * Attach this instance with the mpxio framework
282 	 */
283 	if (mdi_phci_register(vclass + 1, dip, 0) != MDI_SUCCESS) {
284 		cmn_err(CE_WARN, "%s mdi_phci_register failed",
285 		    ddi_node_name(dip));
286 		goto attach_fail;
287 	}
288 	phci_regis++;
289 
290 	if (ddi_create_minor_node(dip, "devctl", S_IFCHR,
291 	    instance, DDI_NT_SCSI_NEXUS, 0) != DDI_SUCCESS) {
292 		cmn_err(CE_NOTE, "%s ddi_create_minor_node failed",
293 		    ddi_node_name(dip));
294 		goto attach_fail;
295 	}
296 
297 	(void) ddi_prop_update_int(DDI_DEV_T_NONE, dip, DDI_NO_AUTODETACH, 1);
298 	ddi_report_dev(dip);
299 	return (DDI_SUCCESS);
300 
301 attach_fail:
302 	if (phci_regis)
303 		(void) mdi_phci_unregister(dip, 0);
304 
305 	ddi_soft_state_free(tphci_state, instance);
306 	return (DDI_FAILURE);
307 }
308 
309 
310 /*ARGSUSED*/
311 static int
312 tphci_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
313 {
314 	int instance = ddi_get_instance(dip);
315 
316 	switch (cmd) {
317 	case DDI_DETACH:
318 		break;
319 
320 	case DDI_SUSPEND:
321 	case DDI_PM_SUSPEND:
322 		return (0);	/* nothing to do */
323 
324 	default:
325 		return (DDI_FAILURE);
326 	}
327 
328 	if (mdi_phci_unregister(dip, 0) != MDI_SUCCESS)
329 		return (DDI_FAILURE);
330 
331 	ddi_remove_minor_node(dip, NULL);
332 	ddi_soft_state_free(tphci_state, instance);
333 
334 	return (DDI_SUCCESS);
335 }
336 
337 /*
338  * tphci_getinfo()
339  * Given the device number, return the devinfo pointer or the
340  * instance number.
341  * Note: always succeed DDI_INFO_DEVT2INSTANCE, even before attach.
342  */
343 
344 /*ARGSUSED*/
345 static int
346 tphci_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result)
347 {
348 	struct tphci_state *phci;
349 	int instance = getminor((dev_t)arg);
350 
351 	switch (cmd) {
352 	case DDI_INFO_DEVT2DEVINFO:
353 		phci = ddi_get_soft_state(tphci_state, instance);
354 		if (phci != NULL)
355 			*result = phci->dip;
356 		else {
357 			*result = NULL;
358 			return (DDI_FAILURE);
359 		}
360 		break;
361 
362 	case DDI_INFO_DEVT2INSTANCE:
363 		*result = (void *)(uintptr_t)instance;
364 		break;
365 
366 	default:
367 		return (DDI_FAILURE);
368 	}
369 
370 	return (DDI_SUCCESS);
371 }
372 
373 /*
374  * Interrupt stuff. NO OP for pseudo drivers.
375  */
376 /*ARGSUSED*/
377 static int
378 tphci_intr_op(dev_info_t *dip, dev_info_t *rdip, ddi_intr_op_t op,
379     ddi_intr_handle_impl_t *hdlp, void *result)
380 {
381 	return (DDI_FAILURE);
382 }
383 
384 static int
385 tphci_ctl(dev_info_t *dip, dev_info_t *rdip,
386 	ddi_ctl_enum_t ctlop, void *arg, void *result)
387 {
388 	switch (ctlop) {
389 	case DDI_CTLOPS_REPORTDEV:
390 		if (rdip == (dev_info_t *)0)
391 			return (DDI_FAILURE);
392 		cmn_err(CE_CONT, "?tphci-device: %s%d\n",
393 		    ddi_get_name(rdip), ddi_get_instance(rdip));
394 		return (DDI_SUCCESS);
395 
396 	case DDI_CTLOPS_INITCHILD:
397 	{
398 		dev_info_t *child = (dev_info_t *)arg;
399 		return (tphci_initchild(dip, child));
400 	}
401 
402 	case DDI_CTLOPS_UNINITCHILD:
403 	{
404 		dev_info_t *child = (dev_info_t *)arg;
405 		return (tphci_uninitchild(dip, child));
406 	}
407 
408 	case DDI_CTLOPS_DMAPMAPC:
409 	case DDI_CTLOPS_REPORTINT:
410 	case DDI_CTLOPS_REGSIZE:
411 	case DDI_CTLOPS_NREGS:
412 	case DDI_CTLOPS_SIDDEV:
413 	case DDI_CTLOPS_SLAVEONLY:
414 	case DDI_CTLOPS_AFFINITY:
415 	case DDI_CTLOPS_POKE:
416 	case DDI_CTLOPS_PEEK:
417 		/*
418 		 * These ops correspond to functions that "shouldn't" be called
419 		 * by a pseudo driver.  So we whine when we're called.
420 		 */
421 		cmn_err(CE_CONT, "%s%d: invalid op (%d) from %s%d\n",
422 		    ddi_get_name(dip), ddi_get_instance(dip),
423 		    ctlop, ddi_get_name(rdip), ddi_get_instance(rdip));
424 		return (DDI_FAILURE);
425 
426 	case DDI_CTLOPS_ATTACH:
427 	case DDI_CTLOPS_BTOP:
428 	case DDI_CTLOPS_BTOPR:
429 	case DDI_CTLOPS_DETACH:
430 	case DDI_CTLOPS_DVMAPAGESIZE:
431 	case DDI_CTLOPS_IOMIN:
432 	case DDI_CTLOPS_POWER:
433 	case DDI_CTLOPS_PTOB:
434 	default:
435 		/*
436 		 * The ops that we pass up (default).  We pass up memory
437 		 * allocation oriented ops that we receive - these may be
438 		 * associated with pseudo HBA drivers below us with target
439 		 * drivers below them that use ddi memory allocation
440 		 * interfaces like scsi_alloc_consistent_buf.
441 		 */
442 		return (ddi_ctlops(dip, rdip, ctlop, arg, result));
443 	}
444 }
445 
446 static int
447 tphci_initchild(dev_info_t *dip, dev_info_t *child)
448 {
449 	_NOTE(ARGUNUSED(dip))
450 	ddi_set_name_addr(child, "0");
451 	return (DDI_SUCCESS);
452 }
453 
454 /*ARGSUSED*/
455 static int
456 tphci_uninitchild(dev_info_t *dip, dev_info_t *child)
457 {
458 	ddi_set_name_addr(child, NULL);
459 	return (DDI_SUCCESS);
460 }
461 
462 static int
463 tp_decode_name(char *devnm, char **cname, char **paddr, char **guid)
464 {
465 	char *tmp;
466 
467 	i_ddi_parse_name(devnm, cname, paddr, NULL);
468 	if ((strcmp(*cname, "tclient") != 0) &&
469 	    (strcmp(*cname, "tphci") != 0) || *paddr == NULL)
470 		return (-1);
471 
472 	tmp = strchr(*paddr, ',');
473 	if (tmp == NULL || tmp[1] == '\0')
474 		return (-1);
475 
476 	*guid = tmp + 1;
477 	return (0);
478 }
479 
480 static int
481 tphci_bus_config(dev_info_t *parent, uint_t flags,
482     ddi_bus_config_op_t op, void *arg, dev_info_t **childp)
483 {
484 	_NOTE(ARGUNUSED(flags))
485 	char		*cname, *paddr, *guid, *devnm;
486 	mdi_pathinfo_t	*pip;
487 	int		len, circ, rval;
488 
489 	switch (op) {
490 	case BUS_CONFIG_ONE:
491 		break;
492 	case BUS_CONFIG_DRIVER:	/* no direct children to configure */
493 	case BUS_CONFIG_ALL:
494 		return (NDI_SUCCESS);
495 	default:
496 		return (NDI_FAILURE);
497 	}
498 
499 	/* only implement BUS_CONFIG_ONE */
500 	devnm = i_ddi_strdup((char *)arg, KM_SLEEP);
501 	len = strlen(devnm) + 1;
502 
503 	/* caddr is hardcoded in the form *,<guid> */
504 	if (tp_decode_name(devnm, &cname, &paddr, &guid) != 0) {
505 		cmn_err(CE_NOTE, "tphci_bus_config -- invalid device %s",
506 		    (char *)arg);
507 		kmem_free(devnm, len);
508 		return (NDI_FAILURE);
509 	}
510 
511 	mdi_devi_enter(parent, &circ);
512 	rval = mdi_pi_alloc(parent, cname, guid, paddr, 0, &pip);
513 	kmem_free(devnm, len);
514 	if (rval != MDI_SUCCESS) {
515 		cmn_err(CE_NOTE, "tphci_bus_config -- mdi_pi_alloc failed");
516 		mdi_devi_exit(parent, circ);
517 		return (NDI_FAILURE);
518 	}
519 
520 	/*
521 	 * Hold the path and exit the pHCI while calling mdi_pi_online
522 	 * to avoid deadlock with power management of pHCI.
523 	 */
524 	mdi_hold_path(pip);
525 	mdi_devi_exit_phci(parent, circ);
526 	rval = mdi_pi_online(pip, 0);
527 	mdi_devi_enter_phci(parent, &circ);
528 	mdi_rele_path(pip);
529 
530 	if (rval != MDI_SUCCESS) {
531 		cmn_err(CE_NOTE, "tphci_bus_config -- mdi_pi_online failed");
532 		(void) mdi_pi_free(pip, 0);
533 		mdi_devi_exit(parent, circ);
534 		return (NDI_FAILURE);
535 	}
536 
537 	if (childp) {
538 		*childp = mdi_pi_get_client(pip);
539 		ndi_hold_devi(*childp);
540 	}
541 	mdi_devi_exit(parent, circ);
542 
543 	return (NDI_SUCCESS);
544 }
545 
546 static int
547 tphci_bus_unconfig(dev_info_t *parent, uint_t flags,
548     ddi_bus_config_op_t op, void *arg)
549 {
550 	int		rval = MDI_SUCCESS;
551 	int		circ;
552 	mdi_pathinfo_t	*pip, *next;
553 	char		*devnm, *cname, *caddr;
554 
555 	switch (op) {
556 	case BUS_UNCONFIG_ONE:
557 		devnm = (char *)arg;
558 		i_ddi_parse_name(devnm, &cname, &caddr, NULL);
559 		if (strcmp(cname, "tclient") != 0)
560 			return (NDI_SUCCESS);	/* no such device */
561 
562 		mdi_devi_enter(parent, &circ);
563 		pip = mdi_pi_find(parent, NULL, caddr);
564 		if (pip) {
565 			mdi_hold_path(pip);
566 			mdi_devi_exit_phci(parent, circ);
567 			rval = mdi_pi_offline(pip, NDI_DEVI_REMOVE);
568 			mdi_devi_enter_phci(parent, &circ);
569 			mdi_rele_path(pip);
570 
571 			if (rval == MDI_SUCCESS)
572 				(void) mdi_pi_free(pip, 0);
573 		}
574 		mdi_devi_exit(parent, circ);
575 		return (rval == MDI_SUCCESS ? NDI_SUCCESS : NDI_FAILURE);
576 
577 	case BUS_UNCONFIG_ALL:
578 		if (flags & NDI_AUTODETACH)
579 			return (NDI_FAILURE);
580 
581 		mdi_devi_enter(parent, &circ);
582 		next = mdi_get_next_client_path(parent, NULL);
583 		while ((pip = next) != NULL) {
584 			next = mdi_get_next_client_path(parent, pip);
585 
586 			mdi_hold_path(pip);
587 			mdi_devi_exit_phci(parent, circ);
588 			rval = mdi_pi_offline(pip, NDI_DEVI_REMOVE);
589 			mdi_devi_enter_phci(parent, &circ);
590 			mdi_rele_path(pip);
591 
592 			if (rval != MDI_SUCCESS)
593 				break;
594 			(void) mdi_pi_free(pip, 0);
595 		}
596 		mdi_devi_exit(parent, circ);
597 		return (rval == MDI_SUCCESS ? NDI_SUCCESS : NDI_FAILURE);
598 
599 	case BUS_UNCONFIG_DRIVER:	/* nothing to do */
600 		return (NDI_SUCCESS);
601 
602 	default:
603 		return (NDI_FAILURE);
604 	}
605 	/*NOTREACHED*/
606 }
607