xref: /titanic_41/usr/src/uts/sun4v/io/vnex.c (revision 108322fb1c3ed341aba9c80c9774df0ed9e35768)
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"	/* SVr4 5.0 */
28 
29 #include <sys/types.h>
30 #include <sys/cmn_err.h>
31 #include <sys/conf.h>
32 #include <sys/ddi_impldefs.h>
33 #include <sys/autoconf.h>
34 #include <sys/systm.h>
35 #include <sys/modctl.h>
36 #include <sys/ddi.h>
37 #include <sys/sunddi.h>
38 #include <sys/ddi_subrdefs.h>
39 #include <sys/promif.h>
40 #include <sys/machsystm.h>
41 #include <sys/ddi_intr_impl.h>
42 #include <sys/hypervisor_api.h>
43 #include <sys/intr.h>
44 
45 #define	SUN4V_REG_SPEC2CFG_HDL(x)	((x >> 32) & ~(0xfull << 28))
46 
47 static kmutex_t vnex_id_lock;
48 /*
49  * Vnex name  to pil map
50  */
51 typedef struct vnex_regspec {
52 	uint64_t physaddr;
53 	uint64_t size;
54 } vnex_regspec_t;
55 
56 struct vnex_pil_map {
57 	caddr_t	name;
58 	uint32_t pil;
59 };
60 
61 /* vnex interrupt descriptor */
62 typedef struct vnex_id {
63 	dev_info_t *vid_dip;
64 	uint32_t vid_ino;
65 	uint64_t vid_ihdl;
66 	uint_t	(*vid_handler)();
67 	caddr_t	vid_arg1;
68 	caddr_t	vid_arg2;
69 	ddi_intr_handle_impl_t *vid_ddi_hdlp;
70 	uint64_t vid_cfg_hdl;
71 	struct vnex_id *vid_next;
72 	struct vnex_id *vid_prev;
73 } vnex_id_t;
74 
75 /* vnex interrupt descriptor list */
76 static vnex_id_t *vnex_id_list;
77 
78 hrtime_t vnex_pending_timeout = 2ull * NANOSEC; /* 2 seconds in nanoseconds */
79 
80 /*
81  * vnex interrupt descriptor list manipulation functions
82  */
83 
84 static vnex_id_t *vnex_locate_id(dev_info_t *dip, uint32_t ino);
85 static vnex_id_t *vnex_alloc_id(dev_info_t *dip, uint32_t ino,
86 	uint64_t dhdl);
87 static void vnex_add_id(vnex_id_t *vid_p);
88 static void vnex_rem_id(vnex_id_t *vid_p);
89 static void vnex_free_id(vnex_id_t *vid_p);
90 
91 uint_t vnex_intr_wrapper(caddr_t arg);
92 
93 static struct vnex_pil_map vnex_name_to_pil[] = {
94 	{"console", 	PIL_12},
95 	{"fma",		PIL_5},
96 	{"echo", 	PIL_3},
97 	{"loop", 	PIL_3},
98 	{"sunmc", 	PIL_3},
99 	{"sunvts", 	PIL_3},
100 	{"explorer", 	PIL_3}
101 };
102 
103 #define	VNEX_MAX_DEVS	(sizeof (vnex_name_to_pil) /	\
104 			    sizeof (struct vnex_pil_map))
105 
106 /*
107  * Config information
108  */
109 static int vnex_intr_ops(dev_info_t *dip, dev_info_t *rdip,
110     ddi_intr_op_t intr_op, ddi_intr_handle_impl_t *hdlp, void *result);
111 
112 static int
113 vnex_ctl(dev_info_t *, dev_info_t *, ddi_ctl_enum_t, void *, void *);
114 
115 static struct bus_ops vnex_bus_ops = {
116 	BUSO_REV,
117 	nullbusmap,
118 	NULL,	/* NO OP */
119 	NULL,	/* NO OP */
120 	NULL,	/* NO OP */
121 	i_ddi_map_fault,
122 	ddi_no_dma_map,
123 	ddi_no_dma_allochdl,
124 	NULL,
125 	NULL,
126 	NULL,
127 	NULL,
128 	NULL,
129 	NULL,
130 	vnex_ctl,
131 	ddi_bus_prop_op,
132 	NULL,	/* (*bus_get_eventcookie)();    */
133 	NULL,	/* (*bus_add_eventcall)();	*/
134 	NULL,	/* (*bus_remove_eventcall)();   */
135 	NULL,	/* (*bus_post_event)();		*/
136 	NULL,	/* (*bus_intr_ctl)();		*/
137 	NULL,	/* (*bus_config)();		*/
138 	NULL,	/* (*bus_unconfig)();		*/
139 	NULL,	/* (*bus_fm_init)();		*/
140 	NULL,	/* (*bus_fm_fini)();		*/
141 	NULL,	/* (*bus_fm_access_enter)();	*/
142 	NULL,	/* (*bus_fm_access_fini)();	*/
143 	NULL,	/* (*bus_power)();		*/
144 	vnex_intr_ops	/* (*bus_intr_op)();	*/
145 };
146 
147 static int vnex_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
148 static int vnex_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
149 
150 static struct dev_ops pseudo_ops = {
151 	DEVO_REV,		/* devo_rev, */
152 	0,			/* refcnt  */
153 	ddi_no_info,		/* info */
154 	nulldev,		/* identify */
155 	nulldev,		/* probe */
156 	vnex_attach,		/* attach */
157 	vnex_detach,		/* detach */
158 	nodev,			/* reset */
159 	(struct cb_ops *)0,	/* driver operations */
160 	&vnex_bus_ops,	/* bus operations */
161 	nulldev			/* power */
162 };
163 
164 /*
165  * Module linkage information for the kernel.
166  */
167 
168 static struct modldrv modldrv = {
169 	&mod_driverops, /* Type of module.  This one is a pseudo driver */
170 	"sun4v virtual-devices nexus driver v%I%",
171 	&pseudo_ops,	/* driver ops */
172 };
173 
174 static struct modlinkage modlinkage = {
175 	MODREV_1, (void *)&modldrv, NULL
176 };
177 
178 int
179 _init(void)
180 {
181 	return (mod_install(&modlinkage));
182 }
183 
184 int
185 _fini(void)
186 {
187 	return (mod_remove(&modlinkage));
188 }
189 
190 int
191 _info(struct modinfo *modinfop)
192 {
193 	return (mod_info(&modlinkage, modinfop));
194 }
195 
196 /*ARGSUSED*/
197 void
198 vnex_intr_dist(void *arg)
199 {
200 	vnex_id_t *vid_p;
201 	uint32_t cpuid;
202 	int	intr_state;
203 	hrtime_t start;
204 
205 	mutex_enter(&vnex_id_lock);
206 
207 	for (vid_p = vnex_id_list; vid_p != NULL;
208 	    vid_p = vid_p->vid_next) {
209 		/*
210 		 * Don't do anything for disabled interrupts.
211 		 * vnex_enable_intr takes care of redistributing interrupts.
212 		 */
213 		if ((hvio_intr_getvalid(vid_p->vid_ihdl,
214 		    &intr_state) == H_EOK) && (intr_state == HV_INTR_NOTVALID))
215 				continue;
216 
217 		cpuid = intr_dist_cpuid();
218 
219 		(void) hvio_intr_setvalid(vid_p->vid_ihdl, HV_INTR_NOTVALID);
220 		/*
221 		 * Make a best effort to wait for pending interrupts to finish.
222 		 * There is not much we can do if we timeout.
223 		 */
224 		start = gethrtime();
225 		while (!panicstr &&
226 		    (hvio_intr_getstate(vid_p->vid_ihdl, &intr_state) ==
227 		    H_EOK) && (intr_state == HV_INTR_DELIVERED_STATE)) {
228 			if (gethrtime() - start > vnex_pending_timeout) {
229 				cmn_err(CE_WARN, "vnex_intr_dist: %s%d "
230 				    "ino 0x%x pending: timedout\n",
231 				    ddi_driver_name(vid_p->vid_dip),
232 				    ddi_get_instance(vid_p->vid_dip),
233 				    vid_p->vid_ino);
234 				break;
235 			}
236 		}
237 		(void) hvio_intr_settarget(vid_p->vid_ihdl, cpuid);
238 		(void) hvio_intr_setvalid(vid_p->vid_ihdl, HV_INTR_VALID);
239 	}
240 	mutex_exit(&vnex_id_lock);
241 }
242 
243 static int
244 vnex_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
245 {
246 	switch (cmd) {
247 	case DDI_ATTACH:
248 		/*
249 		 * Intitialize interrupt descriptor list
250 		 * and mutex.
251 		 */
252 		vnex_id_list = NULL;
253 		mutex_init(&vnex_id_lock, NULL, MUTEX_DRIVER, NULL);
254 		/*
255 		 * Add interrupt redistribution callback.
256 		 */
257 		intr_dist_add(vnex_intr_dist, dip);
258 		return (DDI_SUCCESS);
259 
260 	case DDI_RESUME:
261 		return (DDI_SUCCESS);
262 
263 	default:
264 		return (DDI_FAILURE);
265 	}
266 }
267 
268 /*ARGSUSED*/
269 static int
270 vnex_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
271 {
272 	switch (cmd) {
273 	case DDI_DETACH:
274 		return (DDI_FAILURE);
275 
276 	case DDI_SUSPEND:
277 		return (DDI_SUCCESS);
278 
279 	default:
280 		return (DDI_FAILURE);
281 	}
282 }
283 
284 static int
285 vnex_ctl(dev_info_t *dip, dev_info_t *rdip,
286 	ddi_ctl_enum_t ctlop, void *arg, void *result)
287 {
288 	char	name[12];	/* enough for a decimal integer */
289 	int		reglen;
290 	uint32_t	*vnex_regspec;
291 
292 	switch (ctlop) {
293 	case DDI_CTLOPS_REPORTDEV:
294 		if (rdip == NULL)
295 			return (DDI_FAILURE);
296 		cmn_err(CE_CONT, "?virtual-device: %s%d\n",
297 		    ddi_driver_name(rdip), ddi_get_instance(rdip));
298 		return (DDI_SUCCESS);
299 
300 	case DDI_CTLOPS_INITCHILD:
301 	{
302 		dev_info_t *child = (dev_info_t *)arg;
303 
304 		if (ddi_getlongprop(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS,
305 		    "reg", (caddr_t)&vnex_regspec, &reglen) != DDI_SUCCESS)
306 			return (DDI_FAILURE);
307 
308 		(void) sprintf(name, "%x", *vnex_regspec);
309 		ddi_set_name_addr(child, name);
310 		ddi_set_parent_data(child, NULL);
311 		kmem_free((caddr_t)vnex_regspec, reglen);
312 		return (DDI_SUCCESS);
313 
314 	}
315 
316 	case DDI_CTLOPS_UNINITCHILD:
317 	{
318 		dev_info_t *child = (dev_info_t *)arg;
319 
320 		ddi_set_name_addr(child, NULL);
321 		ddi_remove_minor_node(arg, NULL);
322 		return (DDI_SUCCESS);
323 	}
324 
325 	/*
326 	 * These ops correspond to functions that "shouldn't" be called
327 	 * by a pseudo driver.  So we whinge when we're called.
328 	 */
329 	case DDI_CTLOPS_DMAPMAPC:
330 	case DDI_CTLOPS_REPORTINT:
331 	case DDI_CTLOPS_REGSIZE:
332 	{
333 		*((off_t *)result) = 0;
334 		return (DDI_SUCCESS);
335 	}
336 	case DDI_CTLOPS_NREGS:
337 	{
338 		dev_info_t *child = (dev_info_t *)arg;
339 		if (ddi_getlongprop(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS,
340 		    "reg", (caddr_t)&vnex_regspec, &reglen) != DDI_SUCCESS)
341 			return (DDI_FAILURE);
342 		*((uint_t *)result) = reglen / sizeof (uint32_t);
343 		kmem_free((caddr_t)vnex_regspec, reglen);
344 		return (DDI_SUCCESS);
345 	}
346 	case DDI_CTLOPS_SIDDEV:
347 	case DDI_CTLOPS_SLAVEONLY:
348 	case DDI_CTLOPS_AFFINITY:
349 	case DDI_CTLOPS_IOMIN:
350 	case DDI_CTLOPS_POKE:
351 	case DDI_CTLOPS_PEEK:
352 		cmn_err(CE_CONT, "%s%d: invalid op (%d) from %s%d\n",
353 			ddi_get_name(dip), ddi_get_instance(dip),
354 			ctlop, ddi_get_name(rdip), ddi_get_instance(rdip));
355 		return (DDI_FAILURE);
356 
357 	/*
358 	 * Everything else (e.g. PTOB/BTOP/BTOPR requests) we pass up
359 	 */
360 	default:
361 		return (ddi_ctlops(dip, rdip, ctlop, arg, result));
362 	}
363 }
364 
365 static int
366 vnex_get_pil(dev_info_t *rdip)
367 {
368 	int i;
369 	caddr_t	name;
370 
371 	name = ddi_node_name(rdip);
372 	for (i = 0; i < VNEX_MAX_DEVS; i++) {
373 		if (strcmp(vnex_name_to_pil[i].name,
374 		    name) == 0) {
375 			return (vnex_name_to_pil[i].pil);
376 		}
377 	}
378 	/*
379 	 * if not found pil is 0
380 	 */
381 	return (0);
382 }
383 
384 static int
385 vnex_enable_intr(dev_info_t *rdip, ddi_intr_handle_impl_t *hdlp)
386 {
387 	vnex_id_t *vid_p;
388 	uint32_t cpuid;
389 
390 	vid_p = vnex_locate_id(rdip, hdlp->ih_vector);
391 
392 	ASSERT(vid_p != NULL);
393 
394 	cpuid = intr_dist_cpuid();
395 
396 	if ((hvio_intr_settarget(vid_p->vid_ihdl, cpuid)) != H_EOK) {
397 		return (DDI_FAILURE);
398 	}
399 
400 	if (hvio_intr_setstate(vid_p->vid_ihdl, HV_INTR_IDLE_STATE) != H_EOK) {
401 		return (DDI_FAILURE);
402 	}
403 
404 	if ((hvio_intr_setvalid(vid_p->vid_ihdl, HV_INTR_VALID)) != H_EOK) {
405 		return (DDI_FAILURE);
406 	}
407 
408 	return (DDI_SUCCESS);
409 }
410 
411 static int
412 vnex_disable_intr(dev_info_t *rdip, ddi_intr_handle_impl_t *hdlp)
413 {
414 	vnex_id_t *vid_p;
415 
416 	vid_p = vnex_locate_id(rdip, hdlp->ih_vector);
417 
418 	ASSERT(vid_p != NULL);
419 
420 	if (hvio_intr_setvalid(vid_p->vid_ihdl, HV_INTR_NOTVALID) != H_EOK) {
421 		return (DDI_FAILURE);
422 	}
423 
424 	return (DDI_SUCCESS);
425 }
426 
427 static int
428 vnex_add_intr(dev_info_t *dip, dev_info_t *rdip,
429     ddi_intr_handle_impl_t *hdlp)
430 {
431 	int reglen, ret = DDI_SUCCESS;
432 	vnex_id_t	*vid_p;
433 	uint64_t cfg;
434 	uint32_t ino;
435 	uint64_t ihdl;
436 	vnex_regspec_t *reg_p;
437 
438 	if (ddi_getlongprop(DDI_DEV_T_ANY, dip,
439 	    DDI_PROP_DONTPASS, "reg", (caddr_t)&reg_p,
440 	    &reglen) != DDI_SUCCESS) {
441 		return (DDI_FAILURE);
442 	}
443 
444 	/*
445 	 * get the sun4v config handle for this device
446 	 */
447 
448 	cfg = SUN4V_REG_SPEC2CFG_HDL(reg_p->physaddr);
449 	kmem_free(reg_p, reglen);
450 	ino = hdlp->ih_vector;
451 
452 	/*
453 	 * call hv to get vihdl
454 	 */
455 	if (hvio_intr_devino_to_sysino(cfg, ino, &ihdl) != H_EOK)
456 		return (DDI_FAILURE);
457 
458 	hdlp->ih_vector = ihdl;
459 	/*
460 	 * Allocate a interrupt descriptor (id) with the
461 	 * the interrupt handler and append it to
462 	 * the id list.
463 	 */
464 
465 	vid_p = vnex_alloc_id(rdip, ino, cfg);
466 	vid_p->vid_ihdl = ihdl;
467 	vid_p->vid_handler =  hdlp->ih_cb_func;
468 	vid_p->vid_arg1 =  hdlp->ih_cb_arg1;
469 	vid_p->vid_arg2 =  hdlp->ih_cb_arg2;
470 	vid_p->vid_ddi_hdlp =  hdlp;
471 
472 	DDI_INTR_ASSIGN_HDLR_N_ARGS(hdlp,
473 	    (ddi_intr_handler_t *)vnex_intr_wrapper, (caddr_t)vid_p, NULL);
474 
475 	if (hdlp->ih_pri == 0) {
476 		hdlp->ih_pri = vnex_get_pil(rdip);
477 	}
478 
479 	ret = i_ddi_add_ivintr(hdlp);
480 	if (ret != DDI_SUCCESS) {
481 		return (ret);
482 	}
483 
484 	DDI_INTR_ASSIGN_HDLR_N_ARGS(hdlp, vid_p->vid_handler,
485 	    vid_p->vid_arg1, vid_p->vid_arg2);
486 
487 	return (ret);
488 }
489 
490 static int
491 vnex_remove_intr(dev_info_t *rdip,
492 	ddi_intr_handle_impl_t *hdlp)
493 {
494 	vnex_id_t *vid_p;
495 	uint32_t ino;
496 	int ret = DDI_SUCCESS;
497 
498 	ino = hdlp->ih_vector;
499 	vid_p = vnex_locate_id(rdip, ino);
500 
501 	hdlp->ih_vector = vid_p->vid_ihdl;
502 	i_ddi_rem_ivintr(hdlp);
503 
504 	vnex_free_id(vid_p);
505 
506 	return (ret);
507 }
508 
509 static int
510 vnex_intr_ops(dev_info_t *dip, dev_info_t *rdip,
511     ddi_intr_op_t intr_op, ddi_intr_handle_impl_t *hdlp, void *result)
512 {
513 	int	ret = DDI_SUCCESS;
514 
515 	switch (intr_op) {
516 		case DDI_INTROP_GETCAP:
517 			*(int *)result = DDI_INTR_FLAG_LEVEL;
518 			break;
519 		case DDI_INTROP_ALLOC:
520 			*(int *)result = hdlp->ih_scratch1;
521 			break;
522 		case DDI_INTROP_GETPRI:
523 			*(int *)result = hdlp->ih_pri ?
524 			    hdlp->ih_pri : vnex_get_pil(rdip);
525 			break;
526 		case DDI_INTROP_FREE:
527 			break;
528 		case DDI_INTROP_SETPRI:
529 			break;
530 		case DDI_INTROP_ADDISR:
531 			ret = vnex_add_intr(dip, rdip, hdlp);
532 			break;
533 		case DDI_INTROP_REMISR:
534 			ret = vnex_remove_intr(rdip, hdlp);
535 			break;
536 		case DDI_INTROP_ENABLE:
537 			ret = vnex_enable_intr(rdip, hdlp);
538 			break;
539 		case DDI_INTROP_DISABLE:
540 			ret = vnex_disable_intr(rdip, hdlp);
541 			break;
542 		case DDI_INTROP_NINTRS:
543 		case DDI_INTROP_NAVAIL:
544 			*(int *)result = i_ddi_get_nintrs(rdip);
545 			break;
546 		case DDI_INTROP_SUPPORTED_TYPES:
547 			*(int *)result = i_ddi_get_nintrs(rdip) ?
548 			    DDI_INTR_TYPE_FIXED : 0;
549 			break;
550 		default:
551 			ret = DDI_ENOTSUP;
552 			break;
553 	}
554 
555 	return (ret);
556 }
557 
558 vnex_id_t *
559 vnex_alloc_id(dev_info_t *dip, uint32_t ino, uint64_t dhdl)
560 {
561 	vnex_id_t *vid_p = kmem_alloc(sizeof (vnex_id_t), KM_SLEEP);
562 
563 	vid_p->vid_dip = dip;
564 	vid_p->vid_ino = ino;
565 	vid_p->vid_cfg_hdl = dhdl;
566 
567 	mutex_enter(&vnex_id_lock);
568 	vnex_add_id(vid_p);
569 	mutex_exit(&vnex_id_lock);
570 
571 	return (vid_p);
572 }
573 
574 vnex_id_t *
575 vnex_locate_id(dev_info_t *dip, uint32_t ino)
576 {
577 	vnex_id_t *vid_p;
578 
579 	mutex_enter(&vnex_id_lock);
580 	vid_p = vnex_id_list;
581 
582 	while (vid_p != NULL) {
583 		if (vid_p->vid_dip == dip && vid_p->vid_ino == ino) {
584 			mutex_exit(&vnex_id_lock);
585 			return (vid_p);
586 		}
587 		vid_p = vid_p->vid_next;
588 	}
589 	mutex_exit(&vnex_id_lock);
590 	return (NULL);
591 }
592 
593 static void
594 vnex_free_id(vnex_id_t *vid_p)
595 {
596 	mutex_enter(&vnex_id_lock);
597 	vnex_rem_id(vid_p);
598 	mutex_exit(&vnex_id_lock);
599 
600 	kmem_free(vid_p, sizeof (*vid_p));
601 }
602 
603 static void
604 vnex_rem_id(vnex_id_t *vid_p)
605 {
606 	if (vid_p->vid_prev == NULL) {
607 		vnex_id_list = vid_p->vid_next;
608 		vid_p->vid_prev = NULL;
609 	} else if (vid_p->vid_next == NULL) {
610 		vid_p->vid_prev->vid_next = NULL;
611 	} else {
612 		vid_p->vid_prev->vid_next = vid_p->vid_next;
613 		vid_p->vid_next->vid_prev = vid_p->vid_prev;
614 	}
615 }
616 
617 static void
618 vnex_add_id(vnex_id_t *vid_p)
619 {
620 	if (vnex_id_list == NULL) {
621 		vnex_id_list = vid_p;
622 		vid_p->vid_next = NULL;
623 		vid_p->vid_prev = NULL;
624 		return;
625 	}
626 	/*
627 	 * We always just add to the front of the list
628 	 */
629 	vnex_id_list->vid_prev = vid_p;
630 	vid_p->vid_next = vnex_id_list;
631 	vid_p->vid_prev = NULL;
632 	vnex_id_list = vid_p;
633 }
634 
635 uint_t
636 vnex_intr_wrapper(caddr_t arg)
637 {
638 	vnex_id_t *vid_p = (vnex_id_t *)arg;
639 	int res;
640 	uint_t (*handler)();
641 	caddr_t handler_arg1;
642 	caddr_t handler_arg2;
643 
644 	handler = vid_p->vid_handler;
645 	handler_arg1 = vid_p->vid_arg1;
646 	handler_arg2 = vid_p->vid_arg2;
647 
648 	res = (*handler)(handler_arg1, handler_arg2);
649 
650 	(void) hvio_intr_setstate(vid_p->vid_ihdl, HV_INTR_IDLE_STATE);
651 
652 	return (res);
653 }
654