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