xref: /titanic_50/usr/src/uts/sun4v/io/vnex.c (revision b1dd958f54f8bfa984d306bb8ca8264855761d7b)
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 	ino = hdlp->ih_vector;
402 
403 	/*
404 	 * call hv to get vihdl
405 	 */
406 	if (hvio_intr_devino_to_sysino(cfg, ino, &ihdl) != H_EOK)
407 		return (DDI_FAILURE);
408 
409 	hdlp->ih_vector = ihdl;
410 	/*
411 	 * Allocate a interrupt descriptor (id) with the
412 	 * the interrupt handler and append it to
413 	 * the id list.
414 	 */
415 
416 	vid_p = vnex_alloc_id(rdip, ino, cfg);
417 	vid_p->vid_ihdl = ihdl;
418 	vid_p->vid_handler =  hdlp->ih_cb_func;
419 	vid_p->vid_arg1 =  hdlp->ih_cb_arg1;
420 	vid_p->vid_arg2 =  hdlp->ih_cb_arg2;
421 	vid_p->vid_ddi_hdlp =  hdlp;
422 
423 	DDI_INTR_ASSIGN_HDLR_N_ARGS(hdlp,
424 	    (ddi_intr_handler_t *)vnex_intr_wrapper, (caddr_t)vid_p, NULL);
425 
426 	if (hdlp->ih_pri == 0) {
427 		hdlp->ih_pri = vnex_get_pil(rdip);
428 	}
429 
430 	ret = i_ddi_add_ivintr(hdlp);
431 	if (ret != DDI_SUCCESS) {
432 		return (ret);
433 	}
434 
435 	DDI_INTR_ASSIGN_HDLR_N_ARGS(hdlp, vid_p->vid_handler,
436 	    vid_p->vid_arg1, vid_p->vid_arg2);
437 
438 	return (ret);
439 }
440 
441 static int
442 vnex_remove_intr(dev_info_t *rdip,
443 	ddi_intr_handle_impl_t *hdlp)
444 {
445 	vnex_id_t *vid_p;
446 	uint32_t ino;
447 	int ret = DDI_SUCCESS;
448 
449 	ino = hdlp->ih_vector;
450 	vid_p = vnex_locate_id(rdip, ino);
451 
452 	hdlp->ih_vector = vid_p->vid_ihdl;
453 	i_ddi_rem_ivintr(hdlp);
454 
455 	vnex_free_id(vid_p);
456 
457 	return (ret);
458 }
459 
460 static int
461 vnex_intr_ops(dev_info_t *dip, dev_info_t *rdip,
462     ddi_intr_op_t intr_op, ddi_intr_handle_impl_t *hdlp, void *result)
463 {
464 	ddi_ispec_t *ispecp = (ddi_ispec_t *)hdlp->ih_private;
465 	int ret = DDI_SUCCESS;
466 
467 	switch (intr_op) {
468 		case DDI_INTROP_GETCAP:
469 			*(int *)result = 0;
470 			break;
471 		case DDI_INTROP_ALLOC:
472 			*(int *)result = hdlp->ih_scratch1;
473 			break;
474 		case DDI_INTROP_GETPRI:
475 			*(int *)result = ispecp->is_pil ?
476 			    ispecp->is_pil : vnex_get_pil(rdip);
477 			break;
478 		case DDI_INTROP_FREE:
479 			break;
480 		case DDI_INTROP_SETPRI:
481 			ispecp->is_pil = (*(int *)result);
482 			break;
483 		case DDI_INTROP_ADDISR:
484 			hdlp->ih_vector = *ispecp->is_intr;
485 			ret = vnex_add_intr(dip, rdip, hdlp);
486 			break;
487 		case DDI_INTROP_REMISR:
488 			hdlp->ih_vector = *ispecp->is_intr;
489 			ret = vnex_remove_intr(rdip, hdlp);
490 			break;
491 		case DDI_INTROP_ENABLE:
492 			hdlp->ih_vector = *ispecp->is_intr;
493 			ret = vnex_enable_intr(rdip, hdlp);
494 			break;
495 		case DDI_INTROP_DISABLE:
496 			hdlp->ih_vector = *ispecp->is_intr;
497 			ret = vnex_disable_intr(rdip, hdlp);
498 			break;
499 		case DDI_INTROP_NINTRS:
500 		case DDI_INTROP_NAVAIL:
501 			*(int *)result = i_ddi_get_nintrs(rdip);
502 			break;
503 		case DDI_INTROP_SUPPORTED_TYPES:
504 			*(int *)result = i_ddi_get_nintrs(rdip) ?
505 			    DDI_INTR_TYPE_FIXED : 0;
506 			break;
507 		default:
508 			ret = DDI_ENOTSUP;
509 			break;
510 	}
511 
512 	return (ret);
513 }
514 
515 vnex_id_t *
516 vnex_alloc_id(dev_info_t *dip, uint32_t ino, uint64_t dhdl)
517 {
518 	vnex_id_t *vid_p = kmem_alloc(sizeof (vnex_id_t), KM_SLEEP);
519 
520 	vid_p->vid_dip = dip;
521 	vid_p->vid_ino = ino;
522 	vid_p->vid_cfg_hdl = dhdl;
523 
524 	mutex_enter(&vnex_id_lock);
525 	vnex_add_id(vid_p);
526 	mutex_exit(&vnex_id_lock);
527 
528 	return (vid_p);
529 }
530 
531 vnex_id_t *
532 vnex_locate_id(dev_info_t *dip, uint32_t ino)
533 {
534 	vnex_id_t *vid_p;
535 
536 	mutex_enter(&vnex_id_lock);
537 	vid_p = vnex_id_list;
538 
539 	while (vid_p != NULL) {
540 		if (vid_p->vid_dip == dip && vid_p->vid_ino == ino) {
541 			mutex_exit(&vnex_id_lock);
542 			return (vid_p);
543 		}
544 		vid_p = vid_p->vid_next;
545 	}
546 	mutex_exit(&vnex_id_lock);
547 	return (NULL);
548 }
549 
550 static void
551 vnex_free_id(vnex_id_t *vid_p)
552 {
553 	mutex_enter(&vnex_id_lock);
554 	vnex_rem_id(vid_p);
555 	mutex_exit(&vnex_id_lock);
556 
557 	kmem_free(vid_p, sizeof (*vid_p));
558 }
559 
560 static void
561 vnex_rem_id(vnex_id_t *vid_p)
562 {
563 	if (vid_p->vid_prev == NULL) {
564 		vnex_id_list = vid_p->vid_next;
565 		vid_p->vid_prev = NULL;
566 	} else if (vid_p->vid_next == NULL) {
567 		vid_p->vid_prev->vid_next = NULL;
568 	} else {
569 		vid_p->vid_prev->vid_next = vid_p->vid_next;
570 		vid_p->vid_next->vid_prev = vid_p->vid_prev;
571 	}
572 }
573 
574 static void
575 vnex_add_id(vnex_id_t *vid_p)
576 {
577 	if (vnex_id_list == NULL) {
578 		vnex_id_list = vid_p;
579 		vid_p->vid_next = NULL;
580 		vid_p->vid_prev = NULL;
581 		return;
582 	}
583 	/*
584 	 * We always just add to the front of the list
585 	 */
586 	vnex_id_list->vid_prev = vid_p;
587 	vid_p->vid_next = vnex_id_list;
588 	vid_p->vid_prev = NULL;
589 	vnex_id_list = vid_p;
590 }
591 
592 uint_t
593 vnex_intr_wrapper(caddr_t arg)
594 {
595 	vnex_id_t *vid_p = (vnex_id_t *)arg;
596 	int res;
597 	uint_t (*handler)();
598 	caddr_t handler_arg1;
599 	caddr_t handler_arg2;
600 
601 	handler = vid_p->vid_handler;
602 	handler_arg1 = vid_p->vid_arg1;
603 	handler_arg2 = vid_p->vid_arg2;
604 
605 	res = (*handler)(handler_arg1, handler_arg2);
606 
607 	(void) hvio_intr_setstate(vid_p->vid_ihdl, HV_INTR_IDLE_STATE);
608 
609 	return (res);
610 }
611