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