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