xref: /illumos-gate/usr/src/uts/sun4v/io/vnex.c (revision 814a60b13c0ad90e5d2edfd29a7a84bbf416cc1a)
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/nexusintr_impl.h>
40 #include <sys/promif.h>
41 #include <sys/machsystm.h>
42 #include <sys/ddi_intr_impl.h>
43 #include <sys/hypervisor_api.h>
44 #include <sys/intr.h>
45 
46 #define	SUN4V_REG_SPEC2CFG_HDL(x)	((x >> 32) & ~(0xfull << 28))
47 
48 static kmutex_t vnex_id_lock;
49 /*
50  * Vnex name  to pil map
51  */
52 typedef struct vnex_regspec {
53 	uint64_t physaddr;
54 	uint64_t size;
55 } vnex_regspec_t;
56 
57 struct vnex_pil_map {
58 	caddr_t	name;
59 	uint32_t pil;
60 };
61 
62 /* vnex interrupt descriptor */
63 typedef struct vnex_id {
64 	dev_info_t *vid_dip;
65 	uint32_t vid_ino;
66 	uint64_t vid_ihdl;
67 	uint_t	(*vid_handler)();
68 	caddr_t	vid_arg1;
69 	caddr_t	vid_arg2;
70 	ddi_intr_handle_impl_t *vid_ddi_hdlp;
71 	uint64_t vid_cfg_hdl;
72 	struct vnex_id *vid_next;
73 	struct vnex_id *vid_prev;
74 } vnex_id_t;
75 
76 /* vnex interrupt descriptor list */
77 static vnex_id_t *vnex_id_list;
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 *devi, ddi_attach_cmd_t cmd);
147 static int vnex_detach(dev_info_t *devi, 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 static int
197 vnex_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
198 {
199 	switch (cmd) {
200 	case DDI_ATTACH:
201 		/*
202 		 * Intitialize interrupt descriptor list
203 		 * and mutex.
204 		 */
205 		vnex_id_list = NULL;
206 		mutex_init(&vnex_id_lock, NULL, MUTEX_DRIVER, NULL);
207 		return (DDI_SUCCESS);
208 
209 	case DDI_RESUME:
210 		return (DDI_SUCCESS);
211 
212 	default:
213 		return (DDI_FAILURE);
214 	}
215 }
216 
217 /*ARGSUSED*/
218 static int
219 vnex_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
220 {
221 	switch (cmd) {
222 	case DDI_DETACH:
223 		return (DDI_FAILURE);
224 
225 	case DDI_SUSPEND:
226 		return (DDI_SUCCESS);
227 
228 	default:
229 		return (DDI_FAILURE);
230 	}
231 }
232 
233 static int
234 vnex_ctl(dev_info_t *dip, dev_info_t *rdip,
235 	ddi_ctl_enum_t ctlop, void *arg, void *result)
236 {
237 	char	name[12];	/* enough for a decimal integer */
238 	int		reglen;
239 	uint32_t	*vnex_regspec;
240 
241 	switch (ctlop) {
242 	case DDI_CTLOPS_REPORTDEV:
243 		if (rdip == NULL)
244 			return (DDI_FAILURE);
245 		cmn_err(CE_CONT, "?virtual-device: %s%d\n",
246 		    ddi_driver_name(rdip), ddi_get_instance(rdip));
247 		return (DDI_SUCCESS);
248 
249 	case DDI_CTLOPS_INITCHILD:
250 	{
251 		dev_info_t *child = (dev_info_t *)arg;
252 
253 		if (ddi_getlongprop(DDI_DEV_T_NONE, child, DDI_PROP_DONTPASS,
254 		    "reg", (caddr_t)&vnex_regspec, &reglen) != DDI_SUCCESS)
255 			return (DDI_FAILURE);
256 
257 		(void) sprintf(name, "%x", *vnex_regspec);
258 		ddi_set_name_addr(child, name);
259 		ddi_set_parent_data(child, NULL);
260 		kmem_free((caddr_t)vnex_regspec, reglen);
261 		return (DDI_SUCCESS);
262 
263 	}
264 
265 	case DDI_CTLOPS_UNINITCHILD:
266 	{
267 		dev_info_t *child = (dev_info_t *)arg;
268 
269 		ddi_set_name_addr(child, NULL);
270 		ddi_remove_minor_node(arg, NULL);
271 		return (DDI_SUCCESS);
272 	}
273 
274 	/*
275 	 * These ops correspond to functions that "shouldn't" be called
276 	 * by a pseudo driver.  So we whinge when we're called.
277 	 */
278 	case DDI_CTLOPS_DMAPMAPC:
279 	case DDI_CTLOPS_REPORTINT:
280 	case DDI_CTLOPS_REGSIZE:
281 	{
282 		*((off_t *)result) = 0;
283 		return (DDI_SUCCESS);
284 	}
285 	case DDI_CTLOPS_NREGS:
286 	{
287 		dev_info_t *child = (dev_info_t *)arg;
288 		if (ddi_getlongprop(DDI_DEV_T_NONE, child, DDI_PROP_DONTPASS,
289 		    "reg", (caddr_t)&vnex_regspec, &reglen) != DDI_SUCCESS)
290 			return (DDI_FAILURE);
291 		*((uint_t *)result) = reglen / sizeof (uint32_t);
292 		kmem_free((caddr_t)vnex_regspec, reglen);
293 		return (DDI_SUCCESS);
294 	}
295 	case DDI_CTLOPS_NINTRS:
296 	case DDI_CTLOPS_SIDDEV:
297 	case DDI_CTLOPS_SLAVEONLY:
298 	case DDI_CTLOPS_AFFINITY:
299 	case DDI_CTLOPS_IOMIN:
300 	case DDI_CTLOPS_POKE:
301 	case DDI_CTLOPS_PEEK:
302 	case DDI_CTLOPS_INTR_HILEVEL:
303 	case DDI_CTLOPS_XLATE_INTRS:
304 		cmn_err(CE_CONT, "%s%d: invalid op (%d) from %s%d\n",
305 			ddi_get_name(dip), ddi_get_instance(dip),
306 			ctlop, ddi_get_name(rdip), ddi_get_instance(rdip));
307 		return (DDI_FAILURE);
308 
309 	/*
310 	 * Everything else (e.g. PTOB/BTOP/BTOPR requests) we pass up
311 	 */
312 	default:
313 		return (ddi_ctlops(dip, rdip, ctlop, arg, result));
314 	}
315 }
316 
317 static int
318 vnex_get_pil(dev_info_t *rdip)
319 {
320 	int i;
321 	caddr_t	name;
322 
323 	name = ddi_node_name(rdip);
324 	for (i = 0; i < VNEX_MAX_DEVS; i++) {
325 		if (strcmp(vnex_name_to_pil[i].name,
326 		    name) == 0) {
327 			return (vnex_name_to_pil[i].pil);
328 		}
329 	}
330 	/*
331 	 * if not found pil is 0
332 	 */
333 	return (0);
334 }
335 
336 static int
337 vnex_enable_intr(dev_info_t *rdip, ddi_intr_handle_impl_t *hdlp)
338 {
339 	vnex_id_t *vid_p;
340 	uint32_t cpuid;
341 
342 	vid_p = vnex_locate_id(rdip, hdlp->ih_vector);
343 
344 	ASSERT(vid_p != NULL);
345 
346 	cpuid = intr_dist_cpuid();
347 
348 	if ((hvio_intr_settarget(vid_p->vid_ihdl, cpuid)) != H_EOK) {
349 		return (DDI_FAILURE);
350 	}
351 
352 	if (hvio_intr_setstate(vid_p->vid_ihdl, HV_INTR_IDLE_STATE) != H_EOK) {
353 		return (DDI_FAILURE);
354 	}
355 
356 	if ((hvio_intr_setvalid(vid_p->vid_ihdl, HV_INTR_VALID)) != H_EOK) {
357 		return (DDI_FAILURE);
358 	}
359 
360 	return (DDI_SUCCESS);
361 }
362 
363 static int
364 vnex_disable_intr(dev_info_t *rdip, ddi_intr_handle_impl_t *hdlp)
365 {
366 	vnex_id_t *vid_p;
367 
368 	vid_p = vnex_locate_id(rdip, hdlp->ih_vector);
369 
370 	ASSERT(vid_p != NULL);
371 
372 	if (hvio_intr_setvalid(vid_p->vid_ihdl, HV_INTR_NOTVALID) != H_EOK) {
373 		return (DDI_FAILURE);
374 	}
375 
376 	return (DDI_SUCCESS);
377 }
378 
379 static int
380 vnex_add_intr(dev_info_t *dip, dev_info_t *rdip,
381     ddi_intr_handle_impl_t *hdlp)
382 {
383 	int reglen, ret = DDI_SUCCESS;
384 	vnex_id_t	*vid_p;
385 	uint64_t cfg;
386 	uint32_t ino;
387 	uint64_t ihdl;
388 	vnex_regspec_t *reg_p;
389 
390 	if (ddi_getlongprop(DDI_DEV_T_NONE, dip,
391 	    DDI_PROP_DONTPASS, "reg", (caddr_t)&reg_p,
392 	    &reglen) != DDI_SUCCESS) {
393 		return (DDI_FAILURE);
394 	}
395 
396 	/*
397 	 * get the sun4v config handle for this device
398 	 */
399 
400 	cfg = SUN4V_REG_SPEC2CFG_HDL(reg_p->physaddr);
401 	kmem_free(reg_p, reglen);
402 	ino = hdlp->ih_vector;
403 
404 	/*
405 	 * call hv to get vihdl
406 	 */
407 	if (hvio_intr_devino_to_sysino(cfg, ino, &ihdl) != H_EOK)
408 		return (DDI_FAILURE);
409 
410 	hdlp->ih_vector = ihdl;
411 	/*
412 	 * Allocate a interrupt descriptor (id) with the
413 	 * the interrupt handler and append it to
414 	 * the id list.
415 	 */
416 
417 	vid_p = vnex_alloc_id(rdip, ino, cfg);
418 	vid_p->vid_ihdl = ihdl;
419 	vid_p->vid_handler =  hdlp->ih_cb_func;
420 	vid_p->vid_arg1 =  hdlp->ih_cb_arg1;
421 	vid_p->vid_arg2 =  hdlp->ih_cb_arg2;
422 	vid_p->vid_ddi_hdlp =  hdlp;
423 
424 	DDI_INTR_ASSIGN_HDLR_N_ARGS(hdlp,
425 	    (ddi_intr_handler_t *)vnex_intr_wrapper, (caddr_t)vid_p, NULL);
426 
427 	if (hdlp->ih_pri == 0) {
428 		hdlp->ih_pri = vnex_get_pil(rdip);
429 	}
430 
431 	ret = i_ddi_add_ivintr(hdlp);
432 	if (ret != DDI_SUCCESS) {
433 		return (ret);
434 	}
435 
436 	DDI_INTR_ASSIGN_HDLR_N_ARGS(hdlp, vid_p->vid_handler,
437 	    vid_p->vid_arg1, vid_p->vid_arg2);
438 
439 	return (ret);
440 }
441 
442 static int
443 vnex_remove_intr(dev_info_t *rdip,
444 	ddi_intr_handle_impl_t *hdlp)
445 {
446 	vnex_id_t *vid_p;
447 	uint32_t ino;
448 	int ret = DDI_SUCCESS;
449 
450 	ino = hdlp->ih_vector;
451 	vid_p = vnex_locate_id(rdip, ino);
452 
453 	hdlp->ih_vector = vid_p->vid_ihdl;
454 	i_ddi_rem_ivintr(hdlp);
455 
456 	vnex_free_id(vid_p);
457 
458 	return (ret);
459 }
460 
461 static int
462 vnex_intr_ops(dev_info_t *dip, dev_info_t *rdip,
463     ddi_intr_op_t intr_op, ddi_intr_handle_impl_t *hdlp, void *result)
464 {
465 	ddi_ispec_t *ispecp = (ddi_ispec_t *)hdlp->ih_private;
466 	int ret = DDI_SUCCESS;
467 
468 	switch (intr_op) {
469 		case DDI_INTROP_GETCAP:
470 			*(int *)result = 0;
471 			break;
472 		case DDI_INTROP_ALLOC:
473 			*(int *)result = hdlp->ih_scratch1;
474 			break;
475 		case DDI_INTROP_GETPRI:
476 			*(int *)result = ispecp->is_pil ?
477 			    ispecp->is_pil : vnex_get_pil(rdip);
478 			break;
479 		case DDI_INTROP_FREE:
480 			break;
481 		case DDI_INTROP_SETPRI:
482 			ispecp->is_pil = (*(int *)result);
483 			break;
484 		case DDI_INTROP_ADDISR:
485 			hdlp->ih_vector = *ispecp->is_intr;
486 			ret = vnex_add_intr(dip, rdip, hdlp);
487 			break;
488 		case DDI_INTROP_REMISR:
489 			hdlp->ih_vector = *ispecp->is_intr;
490 			ret = vnex_remove_intr(rdip, hdlp);
491 			break;
492 		case DDI_INTROP_ENABLE:
493 			hdlp->ih_vector = *ispecp->is_intr;
494 			ret = vnex_enable_intr(rdip, hdlp);
495 			break;
496 		case DDI_INTROP_DISABLE:
497 			hdlp->ih_vector = *ispecp->is_intr;
498 			ret = vnex_disable_intr(rdip, hdlp);
499 			break;
500 		case DDI_INTROP_NINTRS:
501 		case DDI_INTROP_NAVAIL:
502 			*(int *)result = i_ddi_get_nintrs(rdip);
503 			break;
504 		case DDI_INTROP_SUPPORTED_TYPES:
505 			*(int *)result = i_ddi_get_nintrs(rdip) ?
506 			    DDI_INTR_TYPE_FIXED : 0;
507 			break;
508 		default:
509 			ret = DDI_ENOTSUP;
510 			break;
511 	}
512 
513 	return (ret);
514 }
515 
516 vnex_id_t *
517 vnex_alloc_id(dev_info_t *dip, uint32_t ino, uint64_t dhdl)
518 {
519 	vnex_id_t *vid_p = kmem_alloc(sizeof (vnex_id_t), KM_SLEEP);
520 
521 	vid_p->vid_dip = dip;
522 	vid_p->vid_ino = ino;
523 	vid_p->vid_cfg_hdl = dhdl;
524 
525 	mutex_enter(&vnex_id_lock);
526 	vnex_add_id(vid_p);
527 	mutex_exit(&vnex_id_lock);
528 
529 	return (vid_p);
530 }
531 
532 vnex_id_t *
533 vnex_locate_id(dev_info_t *dip, uint32_t ino)
534 {
535 	vnex_id_t *vid_p;
536 
537 	mutex_enter(&vnex_id_lock);
538 	vid_p = vnex_id_list;
539 
540 	while (vid_p != NULL) {
541 		if (vid_p->vid_dip == dip && vid_p->vid_ino == ino) {
542 			mutex_exit(&vnex_id_lock);
543 			return (vid_p);
544 		}
545 		vid_p = vid_p->vid_next;
546 	}
547 	mutex_exit(&vnex_id_lock);
548 	return (NULL);
549 }
550 
551 static void
552 vnex_free_id(vnex_id_t *vid_p)
553 {
554 	mutex_enter(&vnex_id_lock);
555 	vnex_rem_id(vid_p);
556 	mutex_exit(&vnex_id_lock);
557 
558 	kmem_free(vid_p, sizeof (*vid_p));
559 }
560 
561 static void
562 vnex_rem_id(vnex_id_t *vid_p)
563 {
564 	if (vid_p->vid_prev == NULL) {
565 		vnex_id_list = vid_p->vid_next;
566 		vid_p->vid_prev = NULL;
567 	} else if (vid_p->vid_next == NULL) {
568 		vid_p->vid_prev->vid_next = NULL;
569 	} else {
570 		vid_p->vid_prev->vid_next = vid_p->vid_next;
571 		vid_p->vid_next->vid_prev = vid_p->vid_prev;
572 	}
573 }
574 
575 static void
576 vnex_add_id(vnex_id_t *vid_p)
577 {
578 	if (vnex_id_list == NULL) {
579 		vnex_id_list = vid_p;
580 		vid_p->vid_next = NULL;
581 		vid_p->vid_prev = NULL;
582 		return;
583 	}
584 	/*
585 	 * We always just add to the front of the list
586 	 */
587 	vnex_id_list->vid_prev = vid_p;
588 	vid_p->vid_next = vnex_id_list;
589 	vid_p->vid_prev = NULL;
590 	vnex_id_list = vid_p;
591 }
592 
593 uint_t
594 vnex_intr_wrapper(caddr_t arg)
595 {
596 	vnex_id_t *vid_p = (vnex_id_t *)arg;
597 	int res;
598 	uint_t (*handler)();
599 	caddr_t handler_arg1;
600 	caddr_t handler_arg2;
601 
602 	handler = vid_p->vid_handler;
603 	handler_arg1 = vid_p->vid_arg1;
604 	handler_arg2 = vid_p->vid_arg2;
605 
606 	res = (*handler)(handler_arg1, handler_arg2);
607 
608 	(void) hvio_intr_setstate(vid_p->vid_ihdl, HV_INTR_IDLE_STATE);
609 
610 	return (res);
611 }
612