xref: /illumos-gate/usr/src/uts/common/io/tphci.c (revision 80ab886d233f514d54c2a6bdeb9fdfd951bd6881)
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_SIDDEV:
415 	case DDI_CTLOPS_SLAVEONLY:
416 	case DDI_CTLOPS_AFFINITY:
417 	case DDI_CTLOPS_POKE:
418 	case DDI_CTLOPS_PEEK:
419 		/*
420 		 * These ops correspond to functions that "shouldn't" be called
421 		 * by a pseudo driver.  So we whine when we're called.
422 		 */
423 		cmn_err(CE_CONT, "%s%d: invalid op (%d) from %s%d\n",
424 			ddi_get_name(dip), ddi_get_instance(dip),
425 			ctlop, ddi_get_name(rdip), ddi_get_instance(rdip));
426 		return (DDI_FAILURE);
427 
428 	case DDI_CTLOPS_ATTACH:
429 	case DDI_CTLOPS_BTOP:
430 	case DDI_CTLOPS_BTOPR:
431 	case DDI_CTLOPS_DETACH:
432 	case DDI_CTLOPS_DVMAPAGESIZE:
433 	case DDI_CTLOPS_IOMIN:
434 	case DDI_CTLOPS_POWER:
435 	case DDI_CTLOPS_PTOB:
436 	default:
437 		/*
438 		 * The ops that we pass up (default).  We pass up memory
439 		 * allocation oriented ops that we receive - these may be
440 		 * associated with pseudo HBA drivers below us with target
441 		 * drivers below them that use ddi memory allocation
442 		 * interfaces like scsi_alloc_consistent_buf.
443 		 */
444 		return (ddi_ctlops(dip, rdip, ctlop, arg, result));
445 	}
446 }
447 
448 static int
449 tphci_initchild(dev_info_t *dip, dev_info_t *child)
450 {
451 	_NOTE(ARGUNUSED(dip))
452 	ddi_set_name_addr(child, "0");
453 	return (DDI_SUCCESS);
454 }
455 
456 /*ARGSUSED*/
457 static int
458 tphci_uninitchild(dev_info_t *dip, dev_info_t *child)
459 {
460 	ddi_set_name_addr(child, NULL);
461 	return (DDI_SUCCESS);
462 }
463 
464 static int
465 tp_decode_name(char *devnm, char **cname, char **paddr, char **guid)
466 {
467 	char *tmp;
468 
469 	i_ddi_parse_name(devnm, cname, paddr, NULL);
470 	if ((strcmp(*cname, "tclient") != 0) &&
471 	    (strcmp(*cname, "tphci") != 0) || *paddr == NULL)
472 		return (-1);
473 
474 	tmp = strchr(*paddr, ',');
475 	if (tmp == NULL || tmp[1] == '\0')
476 		return (-1);
477 
478 	*guid = tmp + 1;
479 	return (0);
480 }
481 
482 static int
483 tphci_bus_config(dev_info_t *parent, uint_t flags,
484     ddi_bus_config_op_t op, void *arg, dev_info_t **childp)
485 {
486 	_NOTE(ARGUNUSED(flags))
487 	char *cname, *paddr, *guid, *devnm;
488 	mdi_pathinfo_t *pip;
489 	int len;
490 
491 	switch (op) {
492 	case BUS_CONFIG_ONE:
493 		break;
494 	case BUS_CONFIG_DRIVER:	/* no direct children to configure */
495 	case BUS_CONFIG_ALL:
496 		return (DDI_SUCCESS);
497 	default:
498 		return (DDI_FAILURE);
499 	}
500 
501 	/* only implement BUS_CONFIG_ONE */
502 	devnm = i_ddi_strdup((char *)arg, KM_SLEEP);
503 	len = strlen(devnm) + 1;
504 
505 	/* caddr is hardcoded in the form *,<guid> */
506 	if (tp_decode_name(devnm, &cname, &paddr, &guid) != 0) {
507 		cmn_err(CE_NOTE, "tphci_bus_config -- invalid device %s",
508 		    (char *)arg);
509 		kmem_free(devnm, len);
510 		return (DDI_FAILURE);
511 	}
512 
513 	if (mdi_pi_alloc(parent, cname, guid, paddr, 0, &pip)
514 	    != MDI_SUCCESS) {
515 		cmn_err(CE_NOTE, "tphci_bus_config -- mdi_pi_alloc failed");
516 		kmem_free(devnm, len);
517 		return (DDI_FAILURE);
518 	}
519 	kmem_free(devnm, len);
520 
521 	if (mdi_pi_online(pip, 0) != MDI_SUCCESS) {
522 		cmn_err(CE_NOTE, "tphci_bus_config -- mdi_pi_online failed");
523 		(void) mdi_pi_free(pip, 0);
524 		return (DDI_FAILURE);
525 	}
526 
527 	if (childp) {
528 		*childp = MDI_PI(pip)->pi_client->ct_dip;
529 		ndi_hold_devi(*childp);
530 	}
531 	return (DDI_SUCCESS);
532 }
533 
534 static int
535 tphci_bus_unconfig(dev_info_t *parent, uint_t flags,
536     ddi_bus_config_op_t op, void *arg)
537 {
538 	int rval, circ;
539 	mdi_pathinfo_t *pip;
540 	mdi_phci_t *ph;
541 	char *devnm, *cname, *caddr;
542 
543 	switch (op) {
544 	case BUS_UNCONFIG_ONE:
545 		devnm = (char *)arg;
546 		i_ddi_parse_name(devnm, &cname, &caddr, NULL);
547 		if (strcmp(cname, "tclient") != 0)
548 			return (DDI_SUCCESS);	/* no such device */
549 		pip = mdi_pi_find(parent, NULL, caddr);
550 		if (pip == NULL)
551 			return (DDI_SUCCESS);
552 		rval = mdi_pi_offline(pip, NDI_DEVI_REMOVE);
553 		return (rval == MDI_SUCCESS ? NDI_SUCCESS : NDI_FAILURE);
554 
555 	case BUS_UNCONFIG_ALL:
556 		if (flags & NDI_AUTODETACH)
557 			return (DDI_FAILURE);
558 
559 		ph = DEVI(parent)->devi_mdi_xhci;
560 		ASSERT(ph != NULL);
561 
562 		rval = MDI_SUCCESS;
563 		ndi_devi_enter(parent, &circ);
564 		pip = (mdi_pathinfo_t *)ph->ph_path_head;
565 		while (pip) {
566 			rval = mdi_pi_offline(pip, NDI_DEVI_REMOVE);
567 			if (rval != MDI_SUCCESS) {
568 				break;
569 			}
570 			pip = (mdi_pathinfo_t *)ph->ph_path_head;
571 		}
572 		ndi_devi_exit(parent, circ);
573 		return (rval == MDI_SUCCESS ? NDI_SUCCESS : NDI_FAILURE);
574 
575 	case BUS_UNCONFIG_DRIVER:	/* nothing to do */
576 		return (DDI_SUCCESS);
577 	default:
578 		return (DDI_FAILURE);
579 	}
580 	/*NOTREACHED*/
581 }
582