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