xref: /illumos-gate/usr/src/uts/sun4v/io/niumx/niumx.c (revision cb343a2e)
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 (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 /*
29  *	Niagara2 Network Interface Unit (NIU) Nexus Driver
30  */
31 
32 #include <sys/conf.h>
33 #include <sys/modctl.h>
34 #include <sys/ddi_impldefs.h>
35 #include <sys/ddi_subrdefs.h>
36 #include <sys/ddi.h>
37 #include <sys/sunndi.h>
38 #include <sys/sunddi.h>
39 #include <sys/open.h>
40 #include <sys/stat.h>
41 #include <sys/file.h>
42 #include <sys/machsystm.h>
43 #include <sys/hsvc.h>
44 #include <sys/sdt.h>
45 #include <sys/hypervisor_api.h>
46 #include "niumx_var.h"
47 
48 static int niumx_fm_init_child(dev_info_t *, dev_info_t *, int,
49 	ddi_iblock_cookie_t *);
50 static int niumx_intr_ops(dev_info_t *dip, dev_info_t *rdip,
51 	ddi_intr_op_t intr_op, ddi_intr_handle_impl_t *hdlp, void *result);
52 static int niumx_attach(dev_info_t *devi, ddi_attach_cmd_t cmd);
53 static int niumx_detach(dev_info_t *devi, ddi_detach_cmd_t cmd);
54 static int niumx_set_intr(dev_info_t *dip, dev_info_t *rdip,
55 	ddi_intr_handle_impl_t *hdlp, int valid);
56 static int niumx_add_intr(dev_info_t *dip, dev_info_t *rdip,
57 	ddi_intr_handle_impl_t *hdlp);
58 static int niumx_rem_intr(dev_info_t *dip, dev_info_t *rdip,
59 	ddi_intr_handle_impl_t *hdlp);
60 static uint_t niumx_intr_hdlr(void *arg);
61 static int niumx_map(dev_info_t *dip, dev_info_t *rdip, ddi_map_req_t *mp,
62 	off_t offset, off_t len, caddr_t *addrp);
63 static int niumx_dma_allochdl(dev_info_t *dip, dev_info_t *rdip,
64 	ddi_dma_attr_t *attrp,
65 	int (*waitfp)(caddr_t), caddr_t arg, ddi_dma_handle_t *handlep);
66 static int niumx_dma_freehdl(dev_info_t *dip, dev_info_t *rdip,
67 	ddi_dma_handle_t handlep);
68 static int niumx_dma_bindhdl(dev_info_t *dip, dev_info_t *rdip,
69 	ddi_dma_handle_t handle, ddi_dma_req_t *dmareq,
70 	ddi_dma_cookie_t *cookiep, uint_t *ccountp);
71 static int niumx_dma_unbindhdl(dev_info_t *dip, dev_info_t *rdip,
72 	ddi_dma_handle_t handle);
73 static int niumx_ctlops(dev_info_t *dip, dev_info_t *rdip,
74 	ddi_ctl_enum_t op, void *arg, void *result);
75 
76 static struct bus_ops niumx_bus_ops = {
77 	BUSO_REV,
78 	niumx_map,
79 	0,
80 	0,
81 	0,
82 	i_ddi_map_fault,
83 	0,
84 	niumx_dma_allochdl,
85 	niumx_dma_freehdl,
86 	niumx_dma_bindhdl,
87 	niumx_dma_unbindhdl,
88 	0,
89 	0,
90 	0,
91 	niumx_ctlops,
92 	ddi_bus_prop_op,
93 	0,				/* (*bus_get_eventcookie)();    */
94 	0,				/* (*bus_add_eventcall)();	*/
95 	0,				/* (*bus_remove_eventcall)();   */
96 	0,				/* (*bus_post_event)();		*/
97 	0,				/* (*bus_intr_ctl)();		*/
98 	0,				/* (*bus_config)(); 		*/
99 	0,				/* (*bus_unconfig)(); 		*/
100 	niumx_fm_init_child,		/* (*bus_fm_init)(); 		*/
101 	0,				/* (*bus_fm_fini)(); 		*/
102 	0,				/* (*bus_enter)()		*/
103 	0,				/* (*bus_exit)()		*/
104 	0,				/* (*bus_power)()		*/
105 	niumx_intr_ops			/* (*bus_intr_op)(); 		*/
106 };
107 
108 static struct dev_ops niumx_ops = {
109 	DEVO_REV,		/* devo_rev */
110 	0,			/* refcnt  */
111 	ddi_no_info,		/* info */
112 	nulldev,		/* identify */
113 	0,			/* probe */
114 	niumx_attach,		/* attach */
115 	niumx_detach,		/* detach */
116 	nulldev,		/* reset */
117 	(struct cb_ops *)0,	/* driver operations */
118 	&niumx_bus_ops,		/* bus operations */
119 	0
120 };
121 
122 /* Module linkage information for the kernel. */
123 static struct modldrv modldrv = {
124 	&mod_driverops, /* Type of module */
125 	"NIU Nexus Driver %I%",
126 	&niumx_ops,	/* driver ops */
127 };
128 
129 static struct modlinkage modlinkage = {
130 	MODREV_1,
131 	(void *)&modldrv,
132 	NULL
133 };
134 
135 static void *niumx_state;
136 static niumx_ih_t niumx_ihtable[NIUMX_MAX_INTRS];
137 
138 /*
139  * forward function declarations:
140  */
141 static void niumx_removechild(dev_info_t *);
142 static int niumx_initchild(dev_info_t *child);
143 
144 int
145 _init(void)
146 {
147 	int e;
148 	if ((e = ddi_soft_state_init(&niumx_state, sizeof (niumx_devstate_t),
149 	    1)) == 0 && (e = mod_install(&modlinkage)) != 0)
150 		ddi_soft_state_fini(&niumx_state);
151 	return (e);
152 }
153 
154 int
155 _fini(void)
156 {
157 	int e;
158 	if ((e = mod_remove(&modlinkage)) == 0)
159 		ddi_soft_state_fini(&niumx_state);
160 	return (e);
161 }
162 
163 int
164 _info(struct modinfo *modinfop)
165 {
166 	return (mod_info(&modlinkage, modinfop));
167 }
168 
169 void
170 niumx_intr_dist(void *arg)
171 {
172 	kmutex_t 	*lock_p = (kmutex_t *)arg;
173 	int		i = NIUMX_RSVD_INTRS;
174 	niumx_ih_t	*ih_p = niumx_ihtable + i;
175 
176 	DBG(DBG_A_INTX, NULL, "niumx_intr_dist entered\n");
177 	mutex_enter(lock_p);
178 	for (; i < NIUMX_MAX_INTRS; i++, ih_p++) {
179 		sysino_t sysino = ih_p->ih_sysino;
180 		cpuid_t	cpuid;
181 		int	intr_state;
182 		if (!sysino ||	/* sequence is significant */
183 		    (hvio_intr_getvalid(sysino, &intr_state) != H_EOK) ||
184 		    (intr_state == HV_INTR_NOTVALID) ||
185 		    (cpuid = intr_dist_cpuid()) == ih_p->ih_cpuid)
186 			continue;
187 
188 		(void) hvio_intr_setvalid(sysino, HV_INTR_NOTVALID);
189 		(void) hvio_intr_settarget(sysino, cpuid);
190 		(void) hvio_intr_setvalid(sysino, HV_INTR_VALID);
191 		ih_p->ih_cpuid = cpuid;
192 	}
193 	mutex_exit(lock_p);
194 }
195 
196 /*
197  * Hypervisor INTR services information for the NIU nexus driver.
198  */
199 static	uint64_t	niumx_intr_min_ver;   /* Neg. API minor version */
200 static hsvc_info_t niumx_hv_intr = {
201 	HSVC_REV_1, NULL, HSVC_GROUP_INTR, NIUMX_INTR_MAJOR_VER,
202 	NIUMX_INTR_MINOR_VER, "NIUMX"
203 };
204 
205 static int
206 niumx_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
207 {
208 	int instance = ddi_get_instance(dip);
209 	niumx_devstate_t *niumxds_p;	/* devstate pointer */
210 	niu_regspec_t	*reg_p;
211 	uint_t		reglen;
212 	int		ret = DDI_SUCCESS;
213 
214 	switch (cmd) {
215 	case DDI_ATTACH:
216 		if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip,
217 			DDI_PROP_DONTPASS, "reg", (int **)&reg_p, &reglen)
218 				!= DDI_PROP_SUCCESS) {
219 			DBG(DBG_ATTACH, dip, "reg lookup failed\n");
220 			ret = DDI_FAILURE;
221 			goto done;
222 		}
223 
224 		/*
225 		 * Allocate and get soft state structure.
226 		 */
227 		if (ddi_soft_state_zalloc(niumx_state, instance)
228 			!= DDI_SUCCESS) {
229 			ret = DDI_FAILURE;
230 			goto prop_free;
231 		}
232 		niumxds_p = (niumx_devstate_t *)ddi_get_soft_state(niumx_state,
233 							instance);
234 		niumxds_p->dip = dip;
235 		mutex_init(&niumxds_p->niumx_mutex, NULL, MUTEX_DRIVER, NULL);
236 
237 		DBG(DBG_ATTACH, dip, "soft state alloc'd instance = %d, "
238 			"niumxds_p = %p\n", instance, niumxds_p);
239 
240 		/*
241 		 * Negotiate the API version for HV INTR services.
242 		 */
243 		if ((ret = hsvc_register(&niumx_hv_intr, &niumx_intr_min_ver))
244 			!= H_EOK) {
245 		    cmn_err(CE_WARN, "%s: cannot negotiate hypervisor services "
246 		    "group: 0x%lx major: 0x%lx minor: 0x%lx errno: %d\n",
247 		    niumx_hv_intr.hsvc_modname, niumx_hv_intr.hsvc_group,
248 		    niumx_hv_intr.hsvc_major, niumx_hv_intr.hsvc_minor, ret);
249 		    ret = DDI_FAILURE;
250 		    goto cleanup;
251 		}
252 
253 		DBG(DBG_ATTACH, dip, "neg. HV API major 0x%lx minor 0x%lx\n",
254 			niumx_hv_intr.hsvc_major, niumx_intr_min_ver);
255 
256 		/* hv devhdl: low 28-bit of 1st "reg" entry's addr.hi */
257 		niumxds_p->niumx_dev_hdl = (devhandle_t)(reg_p->addr_high &
258 			NIUMX_DEVHDLE_MASK);
259 
260 		/* add interrupt redistribution callback */
261 		intr_dist_add(niumx_intr_dist, &niumxds_p->niumx_mutex);
262 
263 		niumxds_p->niumx_fm_cap = DDI_FM_EREPORT_CAPABLE;
264 
265 		ddi_fm_init(niumxds_p->dip, &niumxds_p->niumx_fm_cap,
266 			&niumxds_p->niumx_fm_ibc);
267 
268 		ret = DDI_SUCCESS;
269 		goto prop_free;
270 cleanup:
271 		mutex_destroy(&niumxds_p->niumx_mutex);
272 		ddi_soft_state_free(niumx_state, ddi_get_instance(dip));
273 prop_free:
274 		ddi_prop_free(reg_p);
275 done:
276 		return (ret);
277 
278 	case DDI_RESUME:
279 	default:
280 		break;
281 	}
282 	return (ret);
283 }
284 
285 static int
286 niumx_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
287 {
288 	niumx_devstate_t *niumxds_p;
289 
290 	switch (cmd) {
291 	case DDI_DETACH:
292 		(void) hsvc_unregister(&niumx_hv_intr);
293 
294 		niumxds_p = (niumx_devstate_t *)
295 		    ddi_get_soft_state(niumx_state, ddi_get_instance(dip));
296 
297 		intr_dist_rem(niumx_intr_dist, &niumxds_p->niumx_mutex);
298 		ddi_fm_fini(dip);
299 		mutex_destroy(&niumxds_p->niumx_mutex);
300 		ddi_soft_state_free(niumx_state, ddi_get_instance(dip));
301 		return (DDI_SUCCESS);
302 
303 	case DDI_SUSPEND:
304 	default:
305 		break;
306 	}
307 	return (DDI_FAILURE);
308 }
309 
310 
311 /*
312  * Function used to initialize FMA for our children nodes. Called
313  * through pci busops when child node calls ddi_fm_init.
314  */
315 /*ARGSUSED*/
316 int
317 niumx_fm_init_child(dev_info_t *dip, dev_info_t *cdip, int cap,
318     ddi_iblock_cookie_t *ibc_p)
319 {
320 	niumx_devstate_t	*niumxds_p = DIP_TO_STATE(dip);
321 
322 	ASSERT(ibc_p != NULL);
323 	*ibc_p = niumxds_p->niumx_fm_ibc;
324 
325 	return (niumxds_p->niumx_fm_cap);
326 }
327 
328 
329 /*ARGSUSED*/
330 int
331 niumx_map(dev_info_t *dip, dev_info_t *rdip, ddi_map_req_t *mp,
332 	off_t offset, off_t len, caddr_t *vaddrp)
333 {
334 	struct regspec p_regspec;
335 	ddi_map_req_t p_mapreq;
336 	niu_regspec_t	*reg_p;
337 	int 	i, rn = mp->map_obj.rnumber, reglen, rnglen, rngnum, ret;
338 	niumx_ranges_t	*rng_p;
339 
340 	uint32_t	reg_begin, rng_begin;
341 
342 	DBG(DBG_MAP, dip, "%s%d: mapping %s%d reg %d\n", NAMEINST(dip),
343 		NAMEINST(rdip), rn);
344 
345 	if (ddi_getlongprop(DDI_DEV_T_ANY, rdip, DDI_PROP_DONTPASS,
346 		"reg", (caddr_t)&reg_p, &reglen) != DDI_SUCCESS)
347 		return (DDI_FAILURE);
348 
349 	if (rn < 0 || (rn >= reglen / sizeof (niu_regspec_t))) {
350 		DBG(DBG_MAP, dip,  "rnumber out of range: %d\n", rn);
351 		kmem_free(reg_p, reglen);
352 		return (DDI_ME_RNUMBER_RANGE);
353 	}
354 
355 	/* build regspec up for parent */
356 	p_mapreq = *mp;		/* dup the whole structure */
357 	p_mapreq.map_type = DDI_MT_REGSPEC;
358 	p_mapreq.map_obj.rp = &p_regspec;
359 
360 	if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, "ranges",
361 		(caddr_t)&rng_p, &rnglen) != DDI_SUCCESS) {
362 			DBG(DBG_MAP,  dip, "%s%d: no ranges property\n",
363 				ddi_driver_name(dip), ddi_get_instance(dip));
364 			kmem_free(reg_p, reglen);
365 			return (DDI_FAILURE);
366 	}
367 
368 	/* locate matching ranges record */
369 	rngnum = rnglen / sizeof (niumx_ranges_t);
370 	for (i = 0, reg_p += rn; i < rngnum; rng_p++, i++) {
371 		if (reg_p->addr_high == rng_p->child_hi)
372 			break;
373 	}
374 
375 	if (i >= rngnum) {
376 		DBG(DBG_MAP, dip, "ranges record for reg[%d] not found.\n", rn);
377 		ret = DDI_ME_REGSPEC_RANGE;
378 		goto err;
379 	}
380 
381 	/*
382 	 * validate request has matching bus type and within 4G
383 	 * limit by comparing addr.hi of "ranges" and child "reg".
384 	 */
385 
386 	ASSERT(reg_p->size_high == 0);
387 
388 	rng_begin = rng_p->child_lo;
389 	reg_begin = reg_p->addr_low;
390 	/* check to verify reg bounds are within rng bounds */
391 	if (reg_begin < rng_begin || (reg_begin + (reg_p->size_low - 1)) >
392 			(rng_begin + (rng_p->size_lo - 1))) {
393 		DBG(DBG_MAP, dip, "size out of range for reg[%d].\n", rn);
394 		ret = DDI_ME_REGSPEC_RANGE;
395 		goto err;
396 	}
397 
398 	p_regspec.regspec_bustype = rng_p->parent_hi;
399 	p_regspec.regspec_addr = reg_begin - rng_begin + rng_p->parent_lo;
400 	p_regspec.regspec_size = reg_p->size_low;
401 	DBG(DBG_MAP, dip, "regspec:bus,addr,size = (%x,%x,%x)\n",
402 		p_regspec.regspec_bustype, p_regspec.regspec_addr,
403 		p_regspec.regspec_size);
404 	ret = ddi_map(dip, &p_mapreq, 0, 0, vaddrp);
405 	DBG(DBG_MAP, dip, "niumx_map: ret %d.\n", ret);
406 err:
407 	kmem_free(rng_p - i, rnglen);
408 	kmem_free(reg_p - rn, reglen);
409 	return (ret);
410 }
411 
412 /*
413  * niumx_ctlops
414  */
415 int
416 niumx_ctlops(dev_info_t *dip, dev_info_t *rdip,
417 	ddi_ctl_enum_t ctlop, void *arg, void *result)
418 {
419 	niu_regspec_t *reg_p;
420 	int	reglen, totreg;
421 
422 	DBG(DBG_CTLOPS, dip, "niumx_ctlops ctlop=%d.\n", ctlop);
423 	if (rdip == (dev_info_t *)0)
424 		return (DDI_FAILURE);
425 
426 	switch (ctlop) {
427 	case DDI_CTLOPS_REPORTDEV:
428 		cmn_err(CE_NOTE, "device: %s@%s, %s%d\n",
429 		    ddi_node_name(rdip), ddi_get_name_addr(rdip),
430 		    NAMEINST(rdip));
431 		return (DDI_SUCCESS);
432 
433 	case DDI_CTLOPS_INITCHILD:
434 		return (niumx_initchild((dev_info_t *)arg));
435 
436 	case DDI_CTLOPS_UNINITCHILD:
437 		niumx_removechild((dev_info_t *)arg);
438 		return (DDI_SUCCESS);
439 
440 	case DDI_CTLOPS_REGSIZE:
441 	case DDI_CTLOPS_NREGS:
442 		/* fall through */
443 		break;
444 	default:
445 		DBG(DBG_CTLOPS, dip, "just pass to ddi_cltops.\n");
446 		return (ddi_ctlops(dip, rdip, ctlop, arg, result));
447 	}
448 
449 	/* REGSIZE/NREGS */
450 
451 	*(int *)result = 0;
452 
453 	if (ddi_getlongprop(DDI_DEV_T_NONE, rdip, DDI_PROP_DONTPASS |
454 		DDI_PROP_CANSLEEP, "reg", (caddr_t)&reg_p, &reglen)
455 			!= DDI_SUCCESS)
456 		return (DDI_FAILURE);
457 
458 	totreg = reglen / sizeof (niu_regspec_t);
459 	if (ctlop == DDI_CTLOPS_NREGS) {
460 		DBG(DBG_CTLOPS, (dev_info_t *)dip, "niumx_ctlops NREGS=%d.\n",
461 				totreg);
462 		*(int *)result = totreg;
463 	} else if (ctlop == DDI_CTLOPS_REGSIZE) {
464 		int	rn;
465 		rn = *(int *)arg;
466 		if (rn >= totreg) {
467 			kmem_free(reg_p, reglen);
468 			return (DDI_FAILURE);
469 		}
470 		*(off_t *)result = (reg_p + rn)->size_low;
471 		DBG(DBG_CTLOPS, (dev_info_t *)dip, "rn = %d, REGSIZE=%x.\n",
472 				rn, *(off_t *)result);
473 	}
474 
475 	kmem_free(reg_p, reglen);
476 	return (DDI_SUCCESS);
477 }
478 
479 static int
480 niumx_initchild(dev_info_t *child)
481 {
482 	char name[MAXNAMELEN];
483 	niu_regspec_t *r;
484 	uint_t n;
485 
486 	if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS,
487 	    "reg", (int **)&r, &n) != DDI_SUCCESS) {
488 		return (DDI_FAILURE);
489 	}
490 	(void) snprintf(name, MAXNAMELEN, "%x", (r[0].addr_high &
491 		NIUMX_FUNC_NUM_MASK));
492 	ddi_prop_free(r);
493 	ddi_set_name_addr(child, name);
494 	return (DDI_SUCCESS);
495 }
496 
497 static void
498 niumx_removechild(dev_info_t *dip)
499 {
500 	ddi_set_name_addr(dip, NULL);
501 	ddi_remove_minor_node(dip, NULL);
502 	impl_rem_dev_props(dip);
503 }
504 
505 
506 
507 /*
508  * bus dma alloc handle entry point:
509  */
510 /*ARGSUSED*/
511 int
512 niumx_dma_allochdl(dev_info_t *dip, dev_info_t *rdip, ddi_dma_attr_t *attrp,
513 	int (*waitfp)(caddr_t), caddr_t arg, ddi_dma_handle_t *handlep)
514 {
515 	ddi_dma_impl_t *mp;
516 	int sleep = (waitfp == DDI_DMA_SLEEP) ? KM_SLEEP : KM_NOSLEEP;
517 
518 	DBG(DBG_DMA_ALLOCH, dip, "rdip=%s%d\n", NAMEINST(rdip));
519 
520 	if (attrp->dma_attr_version != DMA_ATTR_V0) {
521 		DBG(DBG_DMA_ALLOCH, (dev_info_t *)dip, "DDI_DMA_BADATTR\n");
522 		return (DDI_DMA_BADATTR);
523 	}
524 
525 	/* Caution: we don't use zalloc to enhance performance! */
526 	if ((mp = kmem_alloc(sizeof (ddi_dma_impl_t), sleep)) == 0) {
527 		DBG(DBG_DMA_ALLOCH, dip, "can't alloc ddi_dma_impl_t\n");
528 		return (DDI_FAILURE);
529 	}
530 	mp->dmai_rdip = rdip;
531 	mp->dmai_pfnlst = NULL;
532 	mp->dmai_cookie = NULL;
533 	mp->dmai_fault = 0;
534 	mp->dmai_fault_check = NULL;
535 	mp->dmai_fault_notify = NULL;
536 
537 	mp->dmai_attr = *attrp; 	/* set requestors attr info */
538 
539 	DBG(DBG_DMA_ALLOCH, dip, "mp=%p\n", mp);
540 
541 	*handlep = (ddi_dma_handle_t)mp;
542 	return (DDI_SUCCESS);
543 }
544 
545 
546 /*
547  * bus dma free handle entry point:
548  */
549 /*ARGSUSED*/
550 int
551 niumx_dma_freehdl(dev_info_t *dip, dev_info_t *rdip, ddi_dma_handle_t handle)
552 {
553 	ddi_dma_impl_t *mp = (ddi_dma_impl_t *)handle;
554 
555 	if (mp->dmai_cookie)
556 		kmem_free(mp->dmai_cookie, sizeof (ddi_dma_cookie_t));
557 	kmem_free(mp, sizeof (ddi_dma_impl_t));
558 
559 	return (DDI_SUCCESS);
560 }
561 
562 
563 /*
564  * bus dma bind handle entry point:
565  *
566  *	check/enforce DMA type, setup pfn0 and some other key pieces
567  *	of this dma request.
568  * Note: this only works with DMA_OTYP_VADDR, and makes use of the known
569  *	fact that only contiguous memory blocks will be passed in.
570  *	Therefore only one cookie will ever be returned.
571  *
572  *	return values:
573  *		DDI_DMA_NOMAPPING - can't get valid pfn0, or bad dma type
574  *		DDI_DMA_NORESOURCES
575  *		DDI_SUCCESS
576  *
577  *	dma handle members affected (set on exit):
578  *	mp->dmai_object		- dmareq->dmar_object
579  *	mp->dmai_rflags		- dmareq->dmar_flags
580  *	mp->dmai_pfn0   	- 1st page pfn (if va/size pair and not shadow)
581  *	mp->dmai_roffset 	- initialized to starting page offset
582  *	mp->dmai_size		- # of total pages of entire object
583  *	mp->dmai_cookie		- new cookie alloc'd
584  */
585 /*ARGSUSED*/
586 int
587 niumx_dma_bindhdl(dev_info_t *dip, dev_info_t *rdip,
588 	ddi_dma_handle_t handle, ddi_dma_req_t *dmareq,
589 	ddi_dma_cookie_t *cookiep, uint_t *ccountp)
590 {
591 	int (*waitfp)(caddr_t) = dmareq->dmar_fp;
592 	ddi_dma_impl_t *mp = (ddi_dma_impl_t *)handle;
593 	ddi_dma_obj_t *dobj_p = &dmareq->dmar_object;
594 	uint32_t offset;
595 	pfn_t pfn0;
596 	int ret;
597 
598 	DBG(DBG_DMA_BINDH, dip, "rdip=%s%d mp=%p dmareq=%p\n", NAMEINST(rdip),
599 		mp, dmareq);
600 
601 	/* first check dma type */
602 	mp->dmai_rflags = dmareq->dmar_flags & DMP_DDIFLAGS | DMP_NOSYNC;
603 	switch (dobj_p->dmao_type) {
604 	case DMA_OTYP_VADDR: {
605 		caddr_t vaddr = dobj_p->dmao_obj.virt_obj.v_addr;
606 		struct as *as_p = dobj_p->dmao_obj.virt_obj.v_as;
607 		struct hat *hat_p = as_p ? as_p->a_hat : kas.a_hat;
608 		offset = (ulong_t)vaddr & NIUMX_PAGE_OFFSET;
609 		pfn0 = hat_getpfnum(hat_p, vaddr);
610 		}
611 		break;
612 
613 	case DMA_OTYP_BUFVADDR:
614 	case DMA_OTYP_PAGES:
615 	case DMA_OTYP_PADDR:
616 	default:
617 		cmn_err(CE_WARN, "%s%d requested unsupported dma type %x",
618 			NAMEINST(mp->dmai_rdip), dobj_p->dmao_type);
619 		ret = DDI_DMA_NOMAPPING;
620 		goto err;
621 	}
622 	if (pfn0 == PFN_INVALID) {
623 		cmn_err(CE_WARN, "%s%d: invalid pfn0 for DMA object %p",
624 			NAMEINST(dip), (void *)dobj_p);
625 		ret = DDI_DMA_NOMAPPING;
626 		goto err;
627 	}
628 	mp->dmai_object	 = *dobj_p;			/* whole object */
629 	mp->dmai_pfn0	 = (void *)pfn0;		/* cache pfn0   */
630 	mp->dmai_roffset = offset;			/* pg0 offset   */
631 	mp->dmai_mapping = mp->dmai_roffset | NIUMX_PTOB(pfn0);
632 	mp->dmai_size = mp->dmai_object.dmao_size;
633 
634 	DBG(DBG_DMA_BINDH, dip, "check pfn: mp=%p pfn0=%x\n",
635 		mp, mp->dmai_pfn0);
636 	if (!(mp->dmai_cookie = kmem_zalloc(sizeof (ddi_dma_cookie_t),
637 		waitfp == DDI_DMA_SLEEP ? KM_SLEEP : KM_NOSLEEP))) {
638 			ret = DDI_DMA_NORESOURCES;
639 			goto err;
640 		}
641 	mp->dmai_cookie->dmac_laddress = mp->dmai_mapping;
642 	mp->dmai_cookie->dmac_size = mp->dmai_size;
643 	*ccountp = 1;
644 	*cookiep = *mp->dmai_cookie;
645 	DBG(DBG_DMA_BINDH, dip, "cookie %" PRIx64 "+%x, count=%d\n",
646 		cookiep->dmac_address, cookiep->dmac_size, *ccountp);
647 	return (DDI_DMA_MAPPED);
648 
649 err:
650 	DBG(DBG_DMA_BINDH, (dev_info_t *)dip,
651 			"niumx_dma_bindhdl error ret=%d\n", ret);
652 	return (ret);
653 }
654 
655 /*
656  * bus dma unbind handle entry point:
657  */
658 /*ARGSUSED*/
659 int
660 niumx_dma_unbindhdl(dev_info_t *dip, dev_info_t *rdip, ddi_dma_handle_t handle)
661 {
662 	ddi_dma_impl_t *mp = (ddi_dma_impl_t *)handle;
663 
664 	DBG(DBG_DMA_UNBINDH, dip, "rdip=%s%d, mp=%p\n",
665 		ddi_driver_name(rdip), ddi_get_instance(rdip), handle);
666 	if (mp->dmai_cookie) {
667 		kmem_free(mp->dmai_cookie, sizeof (ddi_dma_cookie_t));
668 		mp->dmai_cookie = NULL;
669 	}
670 
671 	return (DDI_SUCCESS);
672 }
673 
674 /*ARGSUSED*/
675 int
676 niumx_intr_ops(dev_info_t *dip, dev_info_t *rdip, ddi_intr_op_t intr_op,
677     ddi_intr_handle_impl_t *hdlp, void *result)
678 {
679 
680 	int	ret = DDI_SUCCESS;
681 
682 	DBG(DBG_INTROPS, dip, "niumx_intr_ops: dip=%p rdip=%p intr_op=%x "
683 	    "handle=%p\n", dip, rdip, intr_op, hdlp);
684 
685 	switch (intr_op) {
686 
687 	case DDI_INTROP_SUPPORTED_TYPES:
688 		*(int *)result = DDI_INTR_TYPE_FIXED;
689 		break;
690 	case DDI_INTROP_GETCAP:
691 		*(int *)result =  DDI_INTR_FLAG_LEVEL;
692 		break;
693 	case DDI_INTROP_SETCAP:
694 		ret = DDI_ENOTSUP;
695 		break;
696 	case DDI_INTROP_ALLOC:
697 		/*  scratch1 = count,  # of intrs from DDI framework */
698 		*(int *)result = hdlp->ih_scratch1;
699 		break;
700 	case DDI_INTROP_FREE:
701 		/* Do we need to do anything here?  */
702 		break;
703 	case DDI_INTROP_GETPRI:
704 		*(int *)result = NIUMX_DEFAULT_PIL;
705 		break;
706 	case DDI_INTROP_SETPRI:
707 		ret = DDI_ENOTSUP;
708 		break;
709 	case DDI_INTROP_ADDISR:
710 		ret = niumx_add_intr(dip, rdip, hdlp);
711 		break;
712 	case DDI_INTROP_REMISR:
713 		ret = niumx_rem_intr(dip, rdip, hdlp);
714 		break;
715 	case DDI_INTROP_ENABLE:
716 		ret = niumx_set_intr(dip, rdip, hdlp, HV_INTR_VALID);
717 		break;
718 	case DDI_INTROP_DISABLE:
719 		ret = niumx_set_intr(dip, rdip, hdlp, HV_INTR_NOTVALID);
720 		break;
721 	case DDI_INTROP_SETMASK:
722 		ret = DDI_ENOTSUP;
723 		break;
724 	case DDI_INTROP_CLRMASK:
725 		ret = DDI_ENOTSUP;
726 		break;
727 	case DDI_INTROP_GETPENDING:
728 		ret = DDI_ENOTSUP;
729 		break;
730 	case DDI_INTROP_NINTRS:
731 	case DDI_INTROP_NAVAIL: {
732 		devino_t	*inos_p;
733 		int		inoslen;
734 		if (ddi_getlongprop(DDI_DEV_T_ANY, rdip, DDI_PROP_DONTPASS,
735 			"interrupts", (caddr_t)&inos_p, &inoslen)
736 			!= DDI_SUCCESS) {
737 				ret = DDI_FAILURE;
738 				break;
739 			}
740 		*(int *)result = inoslen / sizeof (uint32_t);
741 		kmem_free(inos_p, inoslen);
742 		}
743 		break;
744 	default:
745 		ret = DDI_ENOTSUP;
746 		break;
747 	}
748 
749 	DBG(DBG_INTROPS, dip, "niumx_intr_ops: ret=%d\n", ret);
750 	return (ret);
751 }
752 
753 int
754 niumx_set_intr(dev_info_t *dip, dev_info_t *rdip,
755     ddi_intr_handle_impl_t *hdlp, int valid)
756 {
757 	niumx_ih_t	*ih_p;
758 	devino_t	*inos_p;
759 	int		inoslen, ret = DDI_SUCCESS;
760 	uint64_t	hvret;
761 
762 	ASSERT(hdlp->ih_inum < NIUMX_MAX_INTRS);
763 
764 	/* find the appropriate slot from the fixed table */
765 	if (ddi_getlongprop(DDI_DEV_T_ANY, rdip, DDI_PROP_DONTPASS,
766 		"interrupts", (caddr_t)&inos_p, &inoslen) != DDI_SUCCESS) {
767 		ret = DDI_FAILURE;
768 		goto fail;
769 	}
770 	ih_p = niumx_ihtable + inos_p[hdlp->ih_inum];
771 	DBG(DBG_A_INTX, dip, "niumx_set_intr: rdip=%s%d, valid=%d %s (%x,%x)\n",
772 		NAMEINST(rdip), valid, valid ? "enabling" : "disabling",
773 		ih_p->ih_inum, ih_p->ih_sysino);
774 
775 	if (valid == HV_INTR_VALID)
776 		(void) hvio_intr_setstate(ih_p->ih_sysino, HV_INTR_IDLE_STATE);
777 	if ((hvret = hvio_intr_setvalid(ih_p->ih_sysino, valid))
778 		!= H_EOK) {
779 		DBG(DBG_A_INTX, dip, "hvio_intr_setvalid failed, ret 0x%x\n",
780 			hvret);
781 		ret = DDI_FAILURE;
782 	}
783 	kmem_free(inos_p, inoslen);
784 fail:
785 	return (ret);
786 }
787 
788 
789 
790 /*
791  * niumx_add_intr:
792  *
793  * This is the leaf/nexus/HV mapping, now read from "interrupts":
794  *
795  * we have a range of 64 to work with:
796  *   [0-15]  - reserved
797  *   [16]    - mac0
798  *   [17]    - MIF
799  *   [18]    - SYSERR
800  *   [19-26] - func0 Rx (qty. 8)
801  *   [27-34] - func0 Tx (qty. 8)
802  *   [35]    - mac1
803  *   [36-43] - func1 Rx (qty. 8)
804  *   [44-51] - func1 Tx (qty. 8)
805  */
806 int
807 niumx_add_intr(dev_info_t *dip, dev_info_t *rdip,
808     ddi_intr_handle_impl_t *hdlp)
809 {
810 	niumx_ih_t	*ih_p;
811 	int		inoslen, ret = DDI_SUCCESS;
812 	uint64_t	hvret;
813 	devino_t	*inos_p, ino; /* INO numbers, from "interrupts" prop */
814 	sysino_t	sysino;
815 
816 	/* get new ino */
817 	if (hdlp->ih_inum >= NIUMX_MAX_INTRS) {
818 		DBG(DBG_INTR, dip, "error: inum %d out of range\n",
819 			hdlp->ih_inum);
820 		ret = DDI_FAILURE;
821 		goto done;
822 	}
823 	if (ddi_getlongprop(DDI_DEV_T_ANY, rdip, DDI_PROP_DONTPASS,
824 		"interrupts", (caddr_t)&inos_p, &inoslen) != DDI_SUCCESS) {
825 		ret = DDI_FAILURE;
826 		goto done;
827 	}
828 	ih_p = niumx_ihtable + inos_p[hdlp->ih_inum];
829 	ino = inos_p[hdlp->ih_inum];
830 	kmem_free(inos_p, inoslen);
831 	if ((hvret = hvio_intr_devino_to_sysino(DIP_TO_HANDLE(dip), ino,
832 		&sysino)) != H_EOK) {
833 		DBG(DBG_INTR, dip, "hvio_intr_devino_to_sysino failed, "
834 			"ret 0x%x\n", hvret);
835 		ret = DDI_FAILURE;
836 		goto done;
837 	}
838 	ih_p->ih_sysino = sysino;
839 	ih_p->ih_dip = dip;
840 	ih_p->ih_inum = hdlp->ih_inum;
841 	ih_p->ih_hdlr = hdlp->ih_cb_func;
842 	ih_p->ih_arg1 = hdlp->ih_cb_arg1;
843 	ih_p->ih_arg2 = hdlp->ih_cb_arg2;
844 
845 	DBG(DBG_A_INTX, dip, "niumx_add_intr: rdip=%s%d inum=0x%x "
846 		"handler=%p arg1=%p arg2=%p, new ih_p = %p\n", NAMEINST(rdip),
847 		hdlp->ih_inum, hdlp->ih_cb_func, hdlp->ih_cb_arg1,
848 		hdlp->ih_cb_arg2, ih_p);
849 
850 	if (hdlp->ih_pri == 0)
851 		hdlp->ih_pri = NIUMX_DEFAULT_PIL;
852 
853 	/* Save sysino value in hdlp */
854 	hdlp->ih_vector = ih_p->ih_sysino;
855 
856 	/* swap in our handler & arg */
857 	DDI_INTR_ASSIGN_HDLR_N_ARGS(hdlp, (ddi_intr_handler_t *)niumx_intr_hdlr,
858 			(void *)ih_p, NULL);
859 
860 	DBG(DBG_A_INTX, dip, "for ino %x adding (%x,%x)\n", ino, ih_p->ih_inum,
861 			ih_p->ih_sysino);
862 	ret = i_ddi_add_ivintr(hdlp);
863 
864 	/* Restore orig. interrupt handler & args in handle. */
865 	DDI_INTR_ASSIGN_HDLR_N_ARGS(hdlp, ih_p->ih_hdlr, ih_p->ih_arg1,
866 		ih_p->ih_arg2);
867 
868 	if (ret != DDI_SUCCESS) {
869 		DBG(DBG_A_INTX, dip, "i_ddi_add_ivintr error ret=%x\n", ret);
870 		goto done;
871 	}
872 
873 	/* select cpu, saving it for removal */
874 	ih_p->ih_cpuid = intr_dist_cpuid();
875 
876 	if ((hvret = hvio_intr_settarget(ih_p->ih_sysino, ih_p->ih_cpuid))
877 		!= H_EOK) {
878 		DBG(DBG_A_INTX, dip, "hvio_intr_settarget failed, ret 0x%x\n",
879 			hvret);
880 		ret = DDI_FAILURE;
881 	}
882 done:
883 	DBG(DBG_A_INTX, dip, "done, ret = %d, ih_p 0x%p, hdlp 0x%p\n", ih_p,
884 		hdlp, ret);
885 	return (ret);
886 }
887 
888 /*
889  * niumx_rem_intr:
890  *
891  * This function is called to unregister interrupts.
892  */
893 int
894 niumx_rem_intr(dev_info_t *dip, dev_info_t *rdip,
895     ddi_intr_handle_impl_t *hdlp)
896 {
897 	niumx_ih_t	*ih_p;
898 	cpuid_t		curr_cpu;
899 	devino_t	*inos_p;
900 	int		inoslen, ret = DDI_SUCCESS;
901 	uint64_t	hvret;
902 
903 	ASSERT(hdlp->ih_inum < NIUMX_MAX_INTRS);
904 
905 	/* find the appropriate slot from the fixed table */
906 	if (ddi_getlongprop(DDI_DEV_T_ANY, rdip, DDI_PROP_DONTPASS,
907 		"interrupts", (caddr_t)&inos_p, &inoslen) != DDI_SUCCESS) {
908 		ret = DDI_FAILURE;
909 		goto fail1;
910 	}
911 	ih_p = niumx_ihtable + inos_p[hdlp->ih_inum];
912 	DBG(DBG_R_INTX, dip, "removing (%x,%x)\n", ih_p->ih_inum,
913 			ih_p->ih_sysino);
914 
915 	/* Get the current cpu */
916 	if ((hvret = hvio_intr_gettarget(ih_p->ih_sysino, &curr_cpu))
917 		!= H_EOK) {
918 		DBG(DBG_R_INTX, dip, "hvio_intr_gettarget failed, ret 0x%x\n",
919 			hvret);
920 		ret = DDI_FAILURE;
921 		goto fail2;
922 	}
923 
924 	intr_dist_cpuid_rem_device_weight(ih_p->ih_cpuid, rdip);
925 
926 	hdlp->ih_vector = ih_p->ih_sysino;
927 	if (hdlp->ih_vector !=  NULL) i_ddi_rem_ivintr(hdlp);
928 
929 fail2:
930 	kmem_free(inos_p, inoslen);
931 fail1:
932 	return (ret);
933 }
934 
935 /*
936  * niumx_intr_hdlr (our interrupt handler)
937  */
938 uint_t
939 niumx_intr_hdlr(void *arg)
940 {
941 	niumx_ih_t *ih_p = (niumx_ih_t *)arg;
942 	uint_t		r;
943 
944 	DTRACE_PROBE4(interrupt__start, dev_info_t, ih_p->ih_dip, void *,
945 		ih_p->ih_hdlr, caddr_t, ih_p->ih_arg1, caddr_t, ih_p->ih_arg2);
946 
947 	r = (*ih_p->ih_hdlr)(ih_p->ih_arg1, ih_p->ih_arg2);
948 
949 	DTRACE_PROBE4(interrupt__complete, dev_info_t, ih_p->ih_dip, void *,
950 		ih_p->ih_hdlr, caddr_t, ih_p->ih_arg1, int, r);
951 
952 	(void) hvio_intr_setstate(ih_p->ih_sysino, HV_INTR_IDLE_STATE);
953 	return (r);
954 }
955 
956 #ifdef	DEBUG
957 uint64_t niumx_debug_flags = 0;
958 
959 static char *niumx_debug_sym [] = {	/* same sequence as niumx_debug_bit */
960 	/*  0 */ "attach",
961 	/*  1 */ "map",
962 	/*  2 */ "nex-ctlops",
963 	/*  3 */ "introps",
964 	/*  4 */ "intr-add",
965 	/*  5 */ "intr-rem",
966 	/*  6 */ "intr",
967 	/*  7 */ "dma-alloc",
968 	/*  8 */ "dma-bind",
969 	/*  9 */ "dma-unbind",
970 	/* 10 */ "chk-dma-mode"
971 };
972 
973 /*ARGSUSED*/
974 void
975 niumx_dbg(niumx_debug_bit_t bit, dev_info_t *dip, char *fmt, ...)
976 {
977 	va_list ap;
978 	char msgbuf[1024];
979 
980 	if (!(1ull << bit & niumx_debug_flags))
981 		return;
982 	va_start(ap, fmt);
983 	(void) vsprintf(msgbuf, fmt, ap);
984 	va_end(ap);
985 	cmn_err(CE_NOTE, "%s: %s", niumx_debug_sym[bit], msgbuf);
986 }
987 
988 #endif	/* DEBUG */
989