xref: /titanic_50/usr/src/uts/sun4v/io/niumx/niumx.c (revision 657f87de670449e1422db4f51fb2880a7cb69d5a)
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 (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
23  */
24 
25 
26 /*
27  *	Niagara2 Network Interface Unit (NIU) Nexus Driver
28  */
29 
30 #include <sys/conf.h>
31 #include <sys/modctl.h>
32 #include <sys/ddi_impldefs.h>
33 #include <sys/ddi_subrdefs.h>
34 #include <sys/ddi.h>
35 #include <sys/sunndi.h>
36 #include <sys/sunddi.h>
37 #include <sys/open.h>
38 #include <sys/stat.h>
39 #include <sys/file.h>
40 #include <sys/machsystm.h>
41 #include <sys/hsvc.h>
42 #include <sys/sdt.h>
43 #include <sys/hypervisor_api.h>
44 #include <sys/cpuvar.h>
45 #include "niumx_var.h"
46 
47 static int niumx_fm_init_child(dev_info_t *, dev_info_t *, int,
48 	ddi_iblock_cookie_t *);
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 int niumxtool_init(dev_info_t *dip);
76 void niumxtool_uninit(dev_info_t *dip);
77 
78 int niumx_get_intr_target(niumx_devstate_t *niumxds_p, niudevino_t ino,
79     niucpuid_t *cpu_id);
80 int niumx_set_intr_target(niumx_devstate_t *niumxds_p, niudevino_t ino,
81     niucpuid_t cpu_id);
82 
83 static struct bus_ops niumx_bus_ops = {
84 	BUSO_REV,
85 	niumx_map,
86 	0,
87 	0,
88 	0,
89 	i_ddi_map_fault,
90 	0,
91 	niumx_dma_allochdl,
92 	niumx_dma_freehdl,
93 	niumx_dma_bindhdl,
94 	niumx_dma_unbindhdl,
95 	0,
96 	0,
97 	0,
98 	niumx_ctlops,
99 	ddi_bus_prop_op,
100 	0,				/* (*bus_get_eventcookie)();    */
101 	0,				/* (*bus_add_eventcall)();	*/
102 	0,				/* (*bus_remove_eventcall)();   */
103 	0,				/* (*bus_post_event)();		*/
104 	0,				/* (*bus_intr_ctl)();		*/
105 	0,				/* (*bus_config)(); 		*/
106 	0,				/* (*bus_unconfig)(); 		*/
107 	niumx_fm_init_child,		/* (*bus_fm_init)(); 		*/
108 	0,				/* (*bus_fm_fini)(); 		*/
109 	0,				/* (*bus_enter)()		*/
110 	0,				/* (*bus_exit)()		*/
111 	0,				/* (*bus_power)()		*/
112 	niumx_intr_ops			/* (*bus_intr_op)(); 		*/
113 };
114 
115 extern  struct cb_ops niumx_cb_ops;
116 
117 static struct dev_ops niumx_ops = {
118 	DEVO_REV,		/* devo_rev */
119 	0,			/* refcnt  */
120 	ddi_no_info,		/* info */
121 	nulldev,		/* identify */
122 	0,			/* probe */
123 	niumx_attach,		/* attach */
124 	niumx_detach,		/* detach */
125 	nulldev,		/* reset */
126 	&niumx_cb_ops,		/* driver operations */
127 	&niumx_bus_ops,		/* bus operations */
128 	0,			/* power */
129 	ddi_quiesce_not_supported,	/* devo_quiesce */
130 };
131 
132 /* Module linkage information for the kernel. */
133 static struct modldrv modldrv = {
134 	&mod_driverops, /* Type of module */
135 	"NIU Nexus Driver",
136 	&niumx_ops,	/* driver ops */
137 };
138 
139 static struct modlinkage modlinkage = {
140 	MODREV_1,
141 	(void *)&modldrv,
142 	NULL
143 };
144 
145 void *niumx_state;
146 
147 /*
148  * forward function declarations:
149  */
150 static void niumx_removechild(dev_info_t *);
151 static int niumx_initchild(dev_info_t *child);
152 
153 int
_init(void)154 _init(void)
155 {
156 	int e;
157 	uint64_t mjrnum;
158 	uint64_t mnrnum;
159 
160 	/*
161 	 * Check HV intr group api versioning.
162 	 * This driver uses the old interrupt routines which are supported
163 	 * in old firmware in the CORE API group and in newer firmware in
164 	 * the INTR API group.  Support for these calls will be dropped
165 	 * once the INTR API group major goes to 2.
166 	 */
167 	if ((hsvc_version(HSVC_GROUP_INTR, &mjrnum, &mnrnum) == 0) &&
168 	    (mjrnum > NIUMX_INTR_MAJOR_VER)) {
169 		cmn_err(CE_WARN, "niumx: unsupported intr api group: "
170 		    "maj:0x%lx, min:0x%lx", mjrnum, mnrnum);
171 		return (ENOTSUP);
172 	}
173 
174 	if ((e = ddi_soft_state_init(&niumx_state, sizeof (niumx_devstate_t),
175 	    1)) == 0 && (e = mod_install(&modlinkage)) != 0)
176 		ddi_soft_state_fini(&niumx_state);
177 	return (e);
178 }
179 
180 int
_fini(void)181 _fini(void)
182 {
183 	int e;
184 	if ((e = mod_remove(&modlinkage)) == 0)
185 		ddi_soft_state_fini(&niumx_state);
186 	return (e);
187 }
188 
189 int
_info(struct modinfo * modinfop)190 _info(struct modinfo *modinfop)
191 {
192 	return (mod_info(&modlinkage, modinfop));
193 }
194 
195 
196 hrtime_t niumx_intr_timeout = 2ull * NANOSEC; /* 2 seconds in nanoseconds */
197 
198 void
niumx_intr_dist(void * arg)199 niumx_intr_dist(void *arg)
200 {
201 	niumx_devstate_t	*niumxds_p = (niumx_devstate_t *)arg;
202 	kmutex_t 	*lock_p = &niumxds_p->niumx_mutex;
203 	int		i;
204 	niumx_ih_t	*ih_p = niumxds_p->niumx_ihtable;
205 
206 	DBG(NIUMX_DBG_A_INTX, NULL, "niumx_intr_dist entered\n");
207 	mutex_enter(lock_p);
208 	for (i = 0; i < NIUMX_MAX_INTRS; i++, ih_p++) {
209 		niusysino_t sysino = ih_p->ih_sysino;
210 		niucpuid_t	cpuid;
211 		int		state;
212 		hrtime_t	start;
213 		dev_info_t	*dip = ih_p->ih_dip;
214 
215 		if (!sysino || (cpuid = intr_dist_cpuid()) == ih_p->ih_cpuid)
216 			continue;
217 
218 		(void) hvio_intr_setvalid(sysino, HV_INTR_NOTVALID);
219 
220 		/* check for pending interrupts, busy wait if so */
221 		for (start = gethrtime(); !panicstr &&
222 		    (hvio_intr_getstate(sysino, &state) == H_EOK) &&
223 		    (state == HV_INTR_DELIVERED_STATE); /* */) {
224 			if (gethrtime() - start > niumx_intr_timeout) {
225 				cmn_err(CE_WARN, "%s%d: niumx_intr_dist: "
226 				    "pending interrupt (%x,%lx) timedout\n",
227 				    ddi_driver_name(dip), ddi_get_instance(dip),
228 				    ih_p->ih_inum, sysino);
229 				(void) hvio_intr_setstate(sysino,
230 				    HV_INTR_IDLE_STATE);
231 				break;
232 			}
233 		}
234 		(void) hvio_intr_settarget(sysino, cpuid);
235 
236 		if (ih_p->ih_state == HV_INTR_VALID)
237 			(void) hvio_intr_setvalid(sysino, HV_INTR_VALID);
238 		else
239 			(void) hvio_intr_setvalid(sysino, HV_INTR_NOTVALID);
240 
241 		ih_p->ih_cpuid = cpuid;
242 	}
243 	mutex_exit(lock_p);
244 }
245 
246 static int
niumx_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)247 niumx_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
248 {
249 	int instance = ddi_get_instance(dip);
250 	niumx_devstate_t *niumxds_p;	/* devstate pointer */
251 	niu_regspec_t	*reg_p;
252 	niumx_ih_t	*ih_p;
253 	uint_t		reglen;
254 	int		i, ret = DDI_SUCCESS;
255 
256 	switch (cmd) {
257 	case DDI_ATTACH:
258 		if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip,
259 		    DDI_PROP_DONTPASS, "reg", (int **)&reg_p, &reglen)
260 		    != DDI_PROP_SUCCESS) {
261 			DBG(NIUMX_DBG_ATTACH, dip, "reg lookup failed\n");
262 			ret = DDI_FAILURE;
263 			goto done;
264 		}
265 
266 		/*
267 		 * Allocate and get soft state structure.
268 		 */
269 		if (ddi_soft_state_zalloc(niumx_state, instance)
270 		    != DDI_SUCCESS) {
271 			ret = DDI_FAILURE;
272 			goto prop_free;
273 		}
274 		niumxds_p = (niumx_devstate_t *)ddi_get_soft_state(niumx_state,
275 		    instance);
276 		niumxds_p->dip = dip;
277 		niumxds_p->niumx_open_count = 0;
278 		mutex_init(&niumxds_p->niumx_mutex, NULL, MUTEX_DRIVER, NULL);
279 
280 		DBG(NIUMX_DBG_ATTACH, dip, "soft state alloc'd instance = %d, "
281 		    "niumxds_p = %p\n", instance, niumxds_p);
282 
283 		/* hv devhdl: low 28-bit of 1st "reg" entry's addr.hi */
284 		niumxds_p->niumx_dev_hdl = (niudevhandle_t)(reg_p->addr_high &
285 		    NIUMX_DEVHDLE_MASK);
286 
287 		ih_p = niumxds_p->niumx_ihtable;
288 		for (i = 0; i < NIUMX_MAX_INTRS; i++, ih_p++) {
289 			ih_p->ih_sysino = 0;
290 			ih_p->ih_state = HV_INTR_NOTVALID;
291 		}
292 
293 		/* add interrupt redistribution callback */
294 		intr_dist_add(niumx_intr_dist, niumxds_p);
295 
296 		niumxds_p->niumx_fm_cap = DDI_FM_EREPORT_CAPABLE;
297 
298 		ddi_fm_init(niumxds_p->dip, &niumxds_p->niumx_fm_cap,
299 		    &niumxds_p->niumx_fm_ibc);
300 
301 		if (niumxtool_init(dip) != DDI_SUCCESS) {
302 			ret = DDI_FAILURE;
303 			goto cleanup;
304 		}
305 
306 		ret = DDI_SUCCESS;
307 		goto prop_free;
308 cleanup:
309 		mutex_destroy(&niumxds_p->niumx_mutex);
310 		ddi_soft_state_free(niumx_state, ddi_get_instance(dip));
311 prop_free:
312 		ddi_prop_free(reg_p);
313 done:
314 		return (ret);
315 
316 	case DDI_RESUME:
317 	default:
318 		break;
319 	}
320 	return (ret);
321 }
322 
323 static int
niumx_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)324 niumx_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
325 {
326 	niumx_devstate_t *niumxds_p;
327 
328 	switch (cmd) {
329 	case DDI_DETACH:
330 
331 		niumxds_p = (niumx_devstate_t *)
332 		    ddi_get_soft_state(niumx_state, ddi_get_instance(dip));
333 
334 		intr_dist_rem(niumx_intr_dist, niumxds_p);
335 		ddi_fm_fini(dip);
336 		niumxtool_uninit(dip);
337 		mutex_destroy(&niumxds_p->niumx_mutex);
338 		ddi_soft_state_free(niumx_state, ddi_get_instance(dip));
339 		return (DDI_SUCCESS);
340 
341 	case DDI_SUSPEND:
342 	default:
343 		break;
344 	}
345 	return (DDI_FAILURE);
346 }
347 
348 
349 /*
350  * Function used to initialize FMA for our children nodes. Called
351  * through pci busops when child node calls ddi_fm_init.
352  */
353 /*ARGSUSED*/
354 int
niumx_fm_init_child(dev_info_t * dip,dev_info_t * cdip,int cap,ddi_iblock_cookie_t * ibc_p)355 niumx_fm_init_child(dev_info_t *dip, dev_info_t *cdip, int cap,
356     ddi_iblock_cookie_t *ibc_p)
357 {
358 	niumx_devstate_t	*niumxds_p = NIUMX_DIP_TO_STATE(dip);
359 
360 	ASSERT(ibc_p != NULL);
361 	*ibc_p = niumxds_p->niumx_fm_ibc;
362 
363 	return (niumxds_p->niumx_fm_cap);
364 }
365 
366 
367 /*ARGSUSED*/
368 int
niumx_map(dev_info_t * dip,dev_info_t * rdip,ddi_map_req_t * mp,off_t offset,off_t len,caddr_t * vaddrp)369 niumx_map(dev_info_t *dip, dev_info_t *rdip, ddi_map_req_t *mp,
370 	off_t offset, off_t len, caddr_t *vaddrp)
371 {
372 	struct regspec p_regspec;
373 	ddi_map_req_t p_mapreq;
374 	niu_regspec_t	*reg_p;
375 	int 	i, rn = mp->map_obj.rnumber, reglen, rnglen, rngnum, ret;
376 	niumx_ranges_t	*rng_p;
377 
378 	uint32_t	reg_begin, rng_begin;
379 
380 	DBG(NIUMX_DBG_MAP, dip, "%s%d: mapping %s%d reg %d\n",
381 	    NIUMX_NAMEINST(dip), NIUMX_NAMEINST(rdip), rn);
382 
383 	if (ddi_getlongprop(DDI_DEV_T_ANY, rdip, DDI_PROP_DONTPASS,
384 	    "reg", (caddr_t)&reg_p, &reglen) != DDI_SUCCESS)
385 		return (DDI_FAILURE);
386 
387 	if (rn < 0 || (rn >= reglen / sizeof (niu_regspec_t))) {
388 		DBG(NIUMX_DBG_MAP, dip,  "rnumber out of range: %d\n", rn);
389 		kmem_free(reg_p, reglen);
390 		return (DDI_ME_RNUMBER_RANGE);
391 	}
392 
393 	/* build regspec up for parent */
394 	p_mapreq = *mp;		/* dup the whole structure */
395 	p_mapreq.map_type = DDI_MT_REGSPEC;
396 	p_mapreq.map_obj.rp = &p_regspec;
397 
398 	if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, "ranges",
399 	    (caddr_t)&rng_p, &rnglen) != DDI_SUCCESS) {
400 			DBG(NIUMX_DBG_MAP,  dip, "%s%d: no ranges property\n",
401 			    ddi_driver_name(dip), ddi_get_instance(dip));
402 			kmem_free(reg_p, reglen);
403 			return (DDI_FAILURE);
404 	}
405 
406 	/* locate matching ranges record */
407 	rngnum = rnglen / sizeof (niumx_ranges_t);
408 	for (i = 0, reg_p += rn; i < rngnum; rng_p++, i++) {
409 		if (reg_p->addr_high == rng_p->child_hi)
410 			break;
411 	}
412 
413 	if (i >= rngnum) {
414 		DBG(NIUMX_DBG_MAP, dip, "ranges record for reg[%d] "
415 		    "not found.\n", rn);
416 		ret = DDI_ME_REGSPEC_RANGE;
417 		goto err;
418 	}
419 
420 	/*
421 	 * validate request has matching bus type and within 4G
422 	 * limit by comparing addr.hi of "ranges" and child "reg".
423 	 */
424 
425 	ASSERT(reg_p->size_high == 0);
426 
427 	rng_begin = rng_p->child_lo;
428 	reg_begin = reg_p->addr_low;
429 	/* check to verify reg bounds are within rng bounds */
430 	if (reg_begin < rng_begin || (reg_begin + (reg_p->size_low - 1)) >
431 	    (rng_begin + (rng_p->size_lo - 1))) {
432 		DBG(NIUMX_DBG_MAP, dip, "size out of range for reg[%d].\n", rn);
433 		ret = DDI_ME_REGSPEC_RANGE;
434 		goto err;
435 	}
436 
437 	p_regspec.regspec_bustype = rng_p->parent_hi;
438 	p_regspec.regspec_addr = reg_begin - rng_begin + rng_p->parent_lo;
439 	p_regspec.regspec_size = reg_p->size_low;
440 	DBG(NIUMX_DBG_MAP, dip, "regspec:bus,addr,size = (%x,%x,%x)\n",
441 	    p_regspec.regspec_bustype, p_regspec.regspec_addr,
442 	    p_regspec.regspec_size);
443 	ret = ddi_map(dip, &p_mapreq, 0, 0, vaddrp);
444 	DBG(NIUMX_DBG_MAP, dip, "niumx_map: ret %d.\n", ret);
445 err:
446 	kmem_free(rng_p - i, rnglen);
447 	kmem_free(reg_p - rn, reglen);
448 	return (ret);
449 }
450 
451 /*
452  * niumx_ctlops
453  */
454 int
niumx_ctlops(dev_info_t * dip,dev_info_t * rdip,ddi_ctl_enum_t ctlop,void * arg,void * result)455 niumx_ctlops(dev_info_t *dip, dev_info_t *rdip,
456 	ddi_ctl_enum_t ctlop, void *arg, void *result)
457 {
458 	niu_regspec_t *reg_p;
459 	int	reglen, totreg;
460 
461 	DBG(NIUMX_DBG_CTLOPS, dip, "niumx_ctlops ctlop=%d.\n", ctlop);
462 	if (rdip == (dev_info_t *)0)
463 		return (DDI_FAILURE);
464 
465 	switch (ctlop) {
466 	case DDI_CTLOPS_REPORTDEV:
467 		cmn_err(CE_NOTE, "device: %s@%s, %s%d\n",
468 		    ddi_node_name(rdip), ddi_get_name_addr(rdip),
469 		    NIUMX_NAMEINST(rdip));
470 		return (DDI_SUCCESS);
471 
472 	case DDI_CTLOPS_INITCHILD:
473 		return (niumx_initchild((dev_info_t *)arg));
474 
475 	case DDI_CTLOPS_UNINITCHILD:
476 		niumx_removechild((dev_info_t *)arg);
477 		return (DDI_SUCCESS);
478 
479 	case DDI_CTLOPS_REGSIZE:
480 	case DDI_CTLOPS_NREGS:
481 		/* fall through */
482 		break;
483 	default:
484 		DBG(NIUMX_DBG_CTLOPS, dip, "just pass to ddi_cltops.\n");
485 		return (ddi_ctlops(dip, rdip, ctlop, arg, result));
486 	}
487 
488 	/* REGSIZE/NREGS */
489 
490 	*(int *)result = 0;
491 
492 	if (ddi_getlongprop(DDI_DEV_T_NONE, rdip, DDI_PROP_DONTPASS |
493 	    DDI_PROP_CANSLEEP, "reg", (caddr_t)&reg_p, &reglen) != DDI_SUCCESS)
494 		return (DDI_FAILURE);
495 
496 	totreg = reglen / sizeof (niu_regspec_t);
497 	if (ctlop == DDI_CTLOPS_NREGS) {
498 		DBG(NIUMX_DBG_CTLOPS, (dev_info_t *)dip,
499 		    "niumx_ctlops NREGS=%d.\n", totreg);
500 		*(int *)result = totreg;
501 	} else if (ctlop == DDI_CTLOPS_REGSIZE) {
502 		int	rn;
503 		rn = *(int *)arg;
504 		if (rn >= totreg) {
505 			kmem_free(reg_p, reglen);
506 			return (DDI_FAILURE);
507 		}
508 		*(off_t *)result = (reg_p + rn)->size_low;
509 		DBG(NIUMX_DBG_CTLOPS, (dev_info_t *)dip,
510 		    "rn = %d, REGSIZE=%x.\n", rn, *(off_t *)result);
511 	}
512 
513 	kmem_free(reg_p, reglen);
514 	return (DDI_SUCCESS);
515 }
516 
517 /*
518  * niumx_name_child
519  *
520  * This function is called from init_child to name a node. It is
521  * also passed as a callback for node merging functions.
522  *
523  * return value: DDI_SUCCESS, DDI_FAILURE
524  */
525 static int
niumx_name_child(dev_info_t * child,char * name,int namelen)526 niumx_name_child(dev_info_t *child, char *name, int namelen)
527 {
528 	niu_regspec_t *r;
529 	uint_t n;
530 
531 	DBG(NIUMX_DBG_CHK_MOD, (dev_info_t *)child, "==> niumx_name_child\n");
532 
533 	if (ndi_dev_is_persistent_node(child) == 0) {
534 		char **unit_addr;
535 
536 		/* name .conf nodes by "unit-address" property */
537 		if (ddi_prop_lookup_string_array(DDI_DEV_T_ANY, child,
538 		    DDI_PROP_DONTPASS, "unit-address", &unit_addr, &n) !=
539 		    DDI_PROP_SUCCESS) {
540 			cmn_err(CE_WARN, "cannot name node from %s.conf",
541 			    ddi_driver_name(child));
542 			return (DDI_FAILURE);
543 		}
544 		if (n != 1 || *unit_addr == NULL || **unit_addr == 0) {
545 			cmn_err(CE_WARN, "unit-address property in %s.conf"
546 			    " not well-formed", ddi_driver_name(child));
547 			ddi_prop_free(unit_addr);
548 			return (DDI_FAILURE);
549 		}
550 
551 		(void) snprintf(name, namelen, "%s", *unit_addr);
552 		ddi_prop_free(unit_addr);
553 		return (DDI_SUCCESS);
554 	}
555 
556 	/* name hardware nodes by "reg" property */
557 	if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS,
558 	    "reg", (int **)&r, &n) != DDI_SUCCESS) {
559 		cmn_err(CE_WARN, "reg property not well-formed");
560 		return (DDI_FAILURE);
561 	}
562 	(void) snprintf(name, namelen, "%x", (r[0].addr_high));
563 	ddi_prop_free(r);
564 	return (DDI_SUCCESS);
565 }
566 
567 static int
niumx_initchild(dev_info_t * child)568 niumx_initchild(dev_info_t *child)
569 {
570 	char name[MAXNAMELEN];
571 
572 	DBG(NIUMX_DBG_CHK_MOD, (dev_info_t *)child, "==> niumx_initchild\n");
573 	/*
574 	 * Non-peristent nodes indicate a prototype node with per-instance
575 	 * properties to be merged into the real h/w device node.
576 	 */
577 	if (ndi_dev_is_persistent_node(child) == 0) {
578 		niu_regspec_t *r;
579 		uint_t n;
580 
581 		if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, child,
582 		    DDI_PROP_DONTPASS, "reg", (int **)&r, &n) ==
583 		    DDI_SUCCESS) {
584 			cmn_err(CE_WARN,
585 			    "cannot merge prototype from %s.conf",
586 			    ddi_driver_name(child));
587 			ddi_prop_free(r);
588 			return (DDI_NOT_WELL_FORMED);
589 		}
590 
591 		if (niumx_name_child(child, name, MAXNAMELEN) != DDI_SUCCESS)
592 			return (DDI_NOT_WELL_FORMED);
593 
594 		ddi_set_name_addr(child, name);
595 		ddi_set_parent_data(child, NULL);
596 
597 		/*
598 		 * Try to merge the properties from this prototype
599 		 * node into real h/w nodes.
600 		 */
601 		if (ndi_merge_node(child, niumx_name_child) == DDI_SUCCESS) {
602 			/*
603 			 * Merged ok - return failure to remove the node.
604 			 */
605 			ddi_set_name_addr(child, NULL);
606 			return (DDI_FAILURE);
607 		}
608 
609 		/*
610 		 * The child was not merged into a h/w node,
611 		 * but there's not much we can do with it other
612 		 * than return failure to cause the node to be removed.
613 		 */
614 		cmn_err(CE_WARN, "!%s@%s: %s.conf properties not merged",
615 		    ddi_driver_name(child), ddi_get_name_addr(child),
616 		    ddi_driver_name(child));
617 		ddi_set_name_addr(child, NULL);
618 		return (DDI_NOT_WELL_FORMED);
619 	}
620 
621 	/*
622 	 * Initialize real h/w nodes
623 	 */
624 	if (niumx_name_child(child, name, MAXNAMELEN) != DDI_SUCCESS)
625 		return (DDI_FAILURE);
626 
627 	ddi_set_name_addr(child, name);
628 	return (DDI_SUCCESS);
629 }
630 
631 static void
niumx_removechild(dev_info_t * dip)632 niumx_removechild(dev_info_t *dip)
633 {
634 	ddi_set_name_addr(dip, NULL);
635 	ddi_remove_minor_node(dip, NULL);
636 	impl_rem_dev_props(dip);
637 }
638 
639 
640 
641 /*
642  * bus dma alloc handle entry point:
643  */
644 /*ARGSUSED*/
645 int
niumx_dma_allochdl(dev_info_t * dip,dev_info_t * rdip,ddi_dma_attr_t * attrp,int (* waitfp)(caddr_t),caddr_t arg,ddi_dma_handle_t * handlep)646 niumx_dma_allochdl(dev_info_t *dip, dev_info_t *rdip, ddi_dma_attr_t *attrp,
647 	int (*waitfp)(caddr_t), caddr_t arg, ddi_dma_handle_t *handlep)
648 {
649 	ddi_dma_impl_t *mp;
650 	int sleep = (waitfp == DDI_DMA_SLEEP) ? KM_SLEEP : KM_NOSLEEP;
651 
652 	DBG(NIUMX_DBG_DMA_ALLOCH, dip, "rdip=%s%d\n", NIUMX_NAMEINST(rdip));
653 
654 	if (attrp->dma_attr_version != DMA_ATTR_V0) {
655 		DBG(NIUMX_DBG_DMA_ALLOCH,
656 		    (dev_info_t *)dip, "DDI_DMA_BADATTR\n");
657 		return (DDI_DMA_BADATTR);
658 	}
659 
660 	/* Caution: we don't use zalloc to enhance performance! */
661 	if ((mp = kmem_alloc(sizeof (ddi_dma_impl_t), sleep)) == 0) {
662 		DBG(NIUMX_DBG_DMA_ALLOCH, dip, "can't alloc ddi_dma_impl_t\n");
663 		return (DDI_FAILURE);
664 	}
665 	mp->dmai_rdip = rdip;
666 	mp->dmai_pfnlst = NULL;
667 	mp->dmai_cookie = NULL;
668 	mp->dmai_fault = 0;
669 	mp->dmai_fault_check = NULL;
670 	mp->dmai_fault_notify = NULL;
671 
672 	mp->dmai_attr = *attrp; 	/* set requestors attr info */
673 
674 	DBG(NIUMX_DBG_DMA_ALLOCH, dip, "mp=%p\n", mp);
675 
676 	*handlep = (ddi_dma_handle_t)mp;
677 	return (DDI_SUCCESS);
678 }
679 
680 
681 /*
682  * bus dma free handle entry point:
683  */
684 /*ARGSUSED*/
685 int
niumx_dma_freehdl(dev_info_t * dip,dev_info_t * rdip,ddi_dma_handle_t handle)686 niumx_dma_freehdl(dev_info_t *dip, dev_info_t *rdip, ddi_dma_handle_t handle)
687 {
688 	ddi_dma_impl_t *mp = (ddi_dma_impl_t *)handle;
689 
690 	if (mp->dmai_cookie)
691 		kmem_free(mp->dmai_cookie, sizeof (ddi_dma_cookie_t));
692 	kmem_free(mp, sizeof (ddi_dma_impl_t));
693 
694 	return (DDI_SUCCESS);
695 }
696 
697 
698 /*
699  * bus dma bind handle entry point:
700  *
701  *	check/enforce DMA type, setup pfn0 and some other key pieces
702  *	of this dma request.
703  * Note: this only works with DMA_OTYP_VADDR, and makes use of the known
704  *	fact that only contiguous memory blocks will be passed in.
705  *	Therefore only one cookie will ever be returned.
706  *
707  *	return values:
708  *		DDI_DMA_NOMAPPING - can't get valid pfn0, or bad dma type
709  *		DDI_DMA_NORESOURCES
710  *		DDI_SUCCESS
711  *
712  *	dma handle members affected (set on exit):
713  *	mp->dmai_object		- dmareq->dmar_object
714  *	mp->dmai_rflags		- dmareq->dmar_flags
715  *	mp->dmai_pfn0   	- 1st page pfn (if va/size pair and not shadow)
716  *	mp->dmai_roffset 	- initialized to starting page offset
717  *	mp->dmai_size		- # of total pages of entire object
718  *	mp->dmai_cookie		- new cookie alloc'd
719  */
720 /*ARGSUSED*/
721 int
niumx_dma_bindhdl(dev_info_t * dip,dev_info_t * rdip,ddi_dma_handle_t handle,ddi_dma_req_t * dmareq,ddi_dma_cookie_t * cookiep,uint_t * ccountp)722 niumx_dma_bindhdl(dev_info_t *dip, dev_info_t *rdip,
723 	ddi_dma_handle_t handle, ddi_dma_req_t *dmareq,
724 	ddi_dma_cookie_t *cookiep, uint_t *ccountp)
725 {
726 	int (*waitfp)(caddr_t) = dmareq->dmar_fp;
727 	ddi_dma_impl_t *mp = (ddi_dma_impl_t *)handle;
728 	ddi_dma_obj_t *dobj_p = &dmareq->dmar_object;
729 	uint32_t offset;
730 	pfn_t pfn0;
731 	int ret;
732 
733 	DBG(NIUMX_DBG_DMA_BINDH, dip, "rdip=%s%d mp=%p dmareq=%p\n",
734 	    NIUMX_NAMEINST(rdip), mp, dmareq);
735 
736 	/* first check dma type */
737 	mp->dmai_rflags = dmareq->dmar_flags & DMP_DDIFLAGS | DMP_NOSYNC;
738 	switch (dobj_p->dmao_type) {
739 	case DMA_OTYP_VADDR: {
740 		caddr_t vaddr = dobj_p->dmao_obj.virt_obj.v_addr;
741 		struct as *as_p = dobj_p->dmao_obj.virt_obj.v_as;
742 		struct hat *hat_p = as_p ? as_p->a_hat : kas.a_hat;
743 		offset = (ulong_t)vaddr & NIUMX_PAGE_OFFSET;
744 		pfn0 = hat_getpfnum(hat_p, vaddr);
745 		}
746 		break;
747 
748 	case DMA_OTYP_BUFVADDR:
749 	case DMA_OTYP_PAGES:
750 	case DMA_OTYP_PADDR:
751 	default:
752 		cmn_err(CE_WARN, "%s%d requested unsupported dma type %x",
753 		    NIUMX_NAMEINST(mp->dmai_rdip), dobj_p->dmao_type);
754 		ret = DDI_DMA_NOMAPPING;
755 		goto err;
756 	}
757 	if (pfn0 == PFN_INVALID) {
758 		cmn_err(CE_WARN, "%s%d: invalid pfn0 for DMA object %p",
759 		    NIUMX_NAMEINST(dip), (void *)dobj_p);
760 		ret = DDI_DMA_NOMAPPING;
761 		goto err;
762 	}
763 	mp->dmai_object	 = *dobj_p;			/* whole object */
764 	mp->dmai_pfn0	 = (void *)pfn0;		/* cache pfn0   */
765 	mp->dmai_roffset = offset;			/* pg0 offset   */
766 	mp->dmai_mapping = mp->dmai_roffset | NIUMX_PTOB(pfn0);
767 	mp->dmai_size = mp->dmai_object.dmao_size;
768 
769 	DBG(NIUMX_DBG_DMA_BINDH, dip, "check pfn: mp=%p pfn0=%x\n",
770 	    mp, mp->dmai_pfn0);
771 	if (!(mp->dmai_cookie = kmem_zalloc(sizeof (ddi_dma_cookie_t),
772 	    waitfp == DDI_DMA_SLEEP ? KM_SLEEP : KM_NOSLEEP))) {
773 			ret = DDI_DMA_NORESOURCES;
774 			goto err;
775 		}
776 	mp->dmai_cookie->dmac_laddress = mp->dmai_mapping;
777 	mp->dmai_cookie->dmac_size = mp->dmai_size;
778 	*ccountp = 1;
779 	*cookiep = *mp->dmai_cookie;
780 	DBG(NIUMX_DBG_DMA_BINDH, dip, "cookie %" PRIx64 "+%x, count=%d\n",
781 	    cookiep->dmac_address, cookiep->dmac_size, *ccountp);
782 	return (DDI_DMA_MAPPED);
783 
784 err:
785 	DBG(NIUMX_DBG_DMA_BINDH, (dev_info_t *)dip,
786 	    "niumx_dma_bindhdl error ret=%d\n", ret);
787 	return (ret);
788 }
789 
790 /*
791  * bus dma unbind handle entry point:
792  */
793 /*ARGSUSED*/
794 int
niumx_dma_unbindhdl(dev_info_t * dip,dev_info_t * rdip,ddi_dma_handle_t handle)795 niumx_dma_unbindhdl(dev_info_t *dip, dev_info_t *rdip, ddi_dma_handle_t handle)
796 {
797 	ddi_dma_impl_t *mp = (ddi_dma_impl_t *)handle;
798 
799 	DBG(NIUMX_DBG_DMA_UNBINDH, dip, "rdip=%s%d, mp=%p\n",
800 	    ddi_driver_name(rdip), ddi_get_instance(rdip), handle);
801 	if (mp->dmai_cookie) {
802 		kmem_free(mp->dmai_cookie, sizeof (ddi_dma_cookie_t));
803 		mp->dmai_cookie = NULL;
804 	}
805 
806 	return (DDI_SUCCESS);
807 }
808 
809 /*ARGSUSED*/
810 int
niumx_intr_ops(dev_info_t * dip,dev_info_t * rdip,ddi_intr_op_t intr_op,ddi_intr_handle_impl_t * hdlp,void * result)811 niumx_intr_ops(dev_info_t *dip, dev_info_t *rdip, ddi_intr_op_t intr_op,
812     ddi_intr_handle_impl_t *hdlp, void *result)
813 {
814 
815 	int	ret = DDI_SUCCESS;
816 
817 	DBG(NIUMX_DBG_INTROPS, dip, "niumx_intr_ops: dip=%p rdip=%p intr_op=%x "
818 	    "handle=%p\n", dip, rdip, intr_op, hdlp);
819 
820 	switch (intr_op) {
821 
822 	case DDI_INTROP_SUPPORTED_TYPES:
823 		*(int *)result = DDI_INTR_TYPE_FIXED;
824 		break;
825 	case DDI_INTROP_GETCAP:
826 		*(int *)result =  DDI_INTR_FLAG_LEVEL;
827 		break;
828 	case DDI_INTROP_SETCAP:
829 		ret = DDI_ENOTSUP;
830 		break;
831 	case DDI_INTROP_ALLOC:
832 		/*  scratch1 = count,  # of intrs from DDI framework */
833 		*(int *)result = hdlp->ih_scratch1;
834 		break;
835 	case DDI_INTROP_FREE:
836 		/* Do we need to do anything here?  */
837 		break;
838 	case DDI_INTROP_GETPRI:
839 		*(int *)result = NIUMX_DEFAULT_PIL;
840 		break;
841 	case DDI_INTROP_SETPRI:
842 		ret = DDI_ENOTSUP;
843 		break;
844 	case DDI_INTROP_ADDISR:
845 		ret = niumx_add_intr(dip, rdip, hdlp);
846 		break;
847 	case DDI_INTROP_REMISR:
848 		ret = niumx_rem_intr(dip, rdip, hdlp);
849 		break;
850 	case DDI_INTROP_ENABLE:
851 		ret = niumx_set_intr(dip, rdip, hdlp, HV_INTR_VALID);
852 		break;
853 	case DDI_INTROP_DISABLE:
854 		ret = niumx_set_intr(dip, rdip, hdlp, HV_INTR_NOTVALID);
855 		break;
856 	case DDI_INTROP_SETMASK:
857 		ret = DDI_ENOTSUP;
858 		break;
859 	case DDI_INTROP_CLRMASK:
860 		ret = DDI_ENOTSUP;
861 		break;
862 	case DDI_INTROP_GETPENDING:
863 		ret = DDI_ENOTSUP;
864 		break;
865 	case DDI_INTROP_NINTRS:
866 	case DDI_INTROP_NAVAIL: {
867 		niudevino_t	*inos_p;
868 		int		inoslen;
869 
870 		if (ddi_getlongprop(DDI_DEV_T_ANY, rdip, DDI_PROP_DONTPASS,
871 		    "interrupts", (caddr_t)&inos_p, &inoslen)
872 		    != DDI_SUCCESS) {
873 			ret = DDI_FAILURE;
874 			break;
875 			}
876 		*(int *)result = inoslen / sizeof (uint32_t);
877 		kmem_free(inos_p, inoslen);
878 		}
879 		break;
880 	case DDI_INTROP_GETTARGET: {
881 		niumx_devstate_t *niumxds_p;
882 
883 		niumxds_p = (niumx_devstate_t *)ddi_get_soft_state(niumx_state,
884 		    ddi_get_instance(dip));
885 
886 		ret = niumx_get_intr_target(niumxds_p, hdlp->ih_vector,
887 		    (niucpuid_t *)result);
888 
889 		}
890 		break;
891 	case DDI_INTROP_SETTARGET: {
892 		niumx_devstate_t *niumxds_p;
893 
894 		niumxds_p = (niumx_devstate_t *)ddi_get_soft_state(niumx_state,
895 		    ddi_get_instance(dip));
896 
897 		ret = niumx_set_intr_target(niumxds_p, hdlp->ih_vector,
898 		    *(niucpuid_t *)result);
899 
900 		}
901 		break;
902 	default:
903 		ret = DDI_ENOTSUP;
904 		break;
905 	}
906 
907 	DBG(NIUMX_DBG_INTROPS, dip, "niumx_intr_ops: ret=%d\n", ret);
908 	return (ret);
909 }
910 
911 int
niumx_set_intr(dev_info_t * dip,dev_info_t * rdip,ddi_intr_handle_impl_t * hdlp,int valid)912 niumx_set_intr(dev_info_t *dip, dev_info_t *rdip,
913     ddi_intr_handle_impl_t *hdlp, int valid)
914 {
915 	niumx_ih_t	*ih_p;
916 	int		ret = DDI_SUCCESS;
917 	uint64_t	hvret;
918 	niumx_devstate_t	*niumxds_p;	/* devstate pointer */
919 	int 		instance = ddi_get_instance(dip);
920 
921 	niumxds_p = (niumx_devstate_t *)ddi_get_soft_state(niumx_state,
922 	    instance);
923 
924 	ASSERT(hdlp->ih_inum < NIUMX_MAX_INTRS);
925 
926 	ih_p = niumxds_p->niumx_ihtable +  hdlp->ih_vector;
927 
928 	DBG(NIUMX_DBG_A_INTX, dip,
929 	    "niumx_set_intr: rdip=%s%d, valid=%d %s (%x,%x)\n",
930 	    NIUMX_NAMEINST(rdip), valid, valid ? "enabling" : "disabling",
931 	    ih_p->ih_inum, ih_p->ih_sysino);
932 
933 	if (valid == HV_INTR_VALID)
934 		(void) hvio_intr_setstate(ih_p->ih_sysino, HV_INTR_IDLE_STATE);
935 	if ((hvret = hvio_intr_setvalid(ih_p->ih_sysino, valid))
936 	    != H_EOK) {
937 		DBG(NIUMX_DBG_A_INTX, dip,
938 		    "hvio_intr_setvalid failed, ret 0x%x\n", hvret);
939 		ret = DDI_FAILURE;
940 	} else
941 		ih_p->ih_state = valid;
942 
943 	return (ret);
944 }
945 
946 int
niumx_get_intr_target(niumx_devstate_t * niumxds_p,niudevino_t ino,niucpuid_t * cpu_id)947 niumx_get_intr_target(niumx_devstate_t *niumxds_p, niudevino_t ino,
948     niucpuid_t *cpu_id)
949 {
950 	niumx_ih_t *ih_p;
951 	niusysino_t sysino;
952 	int rval = DDI_SUCCESS;
953 
954 	ih_p = niumxds_p->niumx_ihtable + ino;
955 
956 	sysino = ih_p->ih_sysino;
957 
958 	if (sysino == 0) {
959 		rval = EINVAL;
960 		goto done;
961 	}
962 
963 	if (hvio_intr_gettarget(sysino, cpu_id) != H_EOK) {
964 		rval = EINVAL;
965 		goto done;
966 	}
967 
968 	if (ih_p->ih_cpuid != *cpu_id)
969 		rval = EIO;
970 
971 done:
972 	return (rval);
973 }
974 
975 int
niumx_set_intr_target(niumx_devstate_t * niumxds_p,niudevino_t ino,niucpuid_t cpu_id)976 niumx_set_intr_target(niumx_devstate_t *niumxds_p, niudevino_t ino,
977     niucpuid_t cpu_id)
978 {
979 	dev_info_t *dip = niumxds_p->dip;
980 	niumx_ih_t *ih_p;
981 	niucpuid_t old_cpu_id;
982 	niusysino_t sysino;
983 	int ret = DDI_SUCCESS;
984 	int state;
985 	hrtime_t start;
986 	extern const int _ncpu;
987 	extern cpu_t *cpu[];
988 
989 	mutex_enter(&cpu_lock);
990 
991 	ih_p = niumxds_p->niumx_ihtable + ino;
992 
993 	sysino = ih_p->ih_sysino;
994 	if (sysino == 0) {
995 		ret = EINVAL;
996 		goto done;
997 	}
998 
999 	if (hvio_intr_gettarget(sysino, &old_cpu_id) != H_EOK) {
1000 		ret = EINVAL;
1001 		goto done;
1002 	}
1003 	if ((cpu_id < _ncpu) && (cpu[cpu_id] && cpu_is_online(cpu[cpu_id]))) {
1004 		if (cpu_id == old_cpu_id)
1005 			goto done;
1006 
1007 		/* check for pending interrupts, busy wait if so */
1008 		for (start = gethrtime(); !panicstr &&
1009 		    (hvio_intr_getstate(sysino, &state) == H_EOK) &&
1010 		    (state == HV_INTR_DELIVERED_STATE); /* */) {
1011 			if (gethrtime() - start > niumx_intr_timeout) {
1012 				cmn_err(CE_WARN, "%s%d: niumx_intr_dist: "
1013 				    "pending interrupt (%x,%lx) timedout\n",
1014 				    ddi_driver_name(dip), ddi_get_instance(dip),
1015 				    ih_p->ih_inum, sysino);
1016 				(void) hvio_intr_setstate(sysino,
1017 				    HV_INTR_IDLE_STATE);
1018 				break;
1019 			}
1020 		}
1021 		(void) hvio_intr_settarget(sysino, cpu_id);
1022 		if (ih_p->ih_state == HV_INTR_VALID)
1023 			(void) hvio_intr_setvalid(sysino, HV_INTR_VALID);
1024 		else
1025 			(void) hvio_intr_setvalid(sysino, HV_INTR_NOTVALID);
1026 		ih_p->ih_cpuid = cpu_id;
1027 	} else {
1028 		ret = DDI_EINVAL;
1029 	}
1030 
1031 done:
1032 	mutex_exit(&cpu_lock);
1033 	return (ret);
1034 }
1035 
1036 
1037 /*
1038  * niumx_add_intr:
1039  *
1040  * This function is called to register interrupts.
1041  */
1042 int
niumx_add_intr(dev_info_t * dip,dev_info_t * rdip,ddi_intr_handle_impl_t * hdlp)1043 niumx_add_intr(dev_info_t *dip, dev_info_t *rdip,
1044     ddi_intr_handle_impl_t *hdlp)
1045 {
1046 	niumx_ih_t	*ih_p;
1047 	int		ret = DDI_SUCCESS;
1048 	uint64_t	hvret;
1049 	niusysino_t	sysino;
1050 	niumx_devstate_t	*niumxds_p;	/* devstate pointer */
1051 	int		instance = ddi_get_instance(dip);
1052 
1053 	niumxds_p = (niumx_devstate_t *)ddi_get_soft_state(niumx_state,
1054 	    instance);
1055 
1056 	/* get new ino */
1057 	if (hdlp->ih_inum >= NIUMX_MAX_INTRS) {
1058 		DBG(NIUMX_DBG_INTR, dip, "error: inum %d out of range\n",
1059 		    hdlp->ih_inum);
1060 		ret = DDI_FAILURE;
1061 		goto done;
1062 	}
1063 
1064 	ih_p = niumxds_p->niumx_ihtable + hdlp->ih_vector;
1065 
1066 	if ((hvret = hvio_intr_devino_to_sysino(NIUMX_DIP_TO_HANDLE(dip),
1067 	    hdlp->ih_vector, &sysino)) != H_EOK) {
1068 		DBG(NIUMX_DBG_INTR, dip, "hvio_intr_devino_to_sysino failed, "
1069 		    "ret 0x%x\n", hvret);
1070 		ret = DDI_FAILURE;
1071 		goto done;
1072 	}
1073 	ih_p->ih_sysino = sysino;
1074 	ih_p->ih_dip = rdip;
1075 	ih_p->ih_inum = hdlp->ih_inum;
1076 	ih_p->ih_hdlr = hdlp->ih_cb_func;
1077 	ih_p->ih_arg1 = hdlp->ih_cb_arg1;
1078 	ih_p->ih_arg2 = hdlp->ih_cb_arg2;
1079 
1080 	DBG(NIUMX_DBG_A_INTX, dip, "niumx_add_intr: rdip=%s%d inum=0x%x "
1081 	    "handler=%p arg1=%p arg2=%p, new ih_p = %p\n", NIUMX_NAMEINST(rdip),
1082 	    hdlp->ih_inum, hdlp->ih_cb_func, hdlp->ih_cb_arg1,
1083 	    hdlp->ih_cb_arg2, ih_p);
1084 
1085 	if (hdlp->ih_pri == 0)
1086 		hdlp->ih_pri = NIUMX_DEFAULT_PIL;
1087 
1088 	ih_p->ih_pri = hdlp->ih_pri;
1089 
1090 	DBG(NIUMX_DBG_A_INTX, dip, "for ino %x adding (%x,%x)\n",
1091 	    hdlp->ih_vector, ih_p->ih_inum, ih_p->ih_sysino);
1092 
1093 	/* Save sysino value in hdlp */
1094 	hdlp->ih_vector = ih_p->ih_sysino;
1095 
1096 	/* swap in our handler & arg */
1097 	DDI_INTR_ASSIGN_HDLR_N_ARGS(hdlp, (ddi_intr_handler_t *)niumx_intr_hdlr,
1098 	    (void *)ih_p, NULL);
1099 
1100 	ret = i_ddi_add_ivintr(hdlp);
1101 
1102 	/* Restore orig. interrupt handler & args in handle. */
1103 	DDI_INTR_ASSIGN_HDLR_N_ARGS(hdlp, ih_p->ih_hdlr, ih_p->ih_arg1,
1104 	    ih_p->ih_arg2);
1105 
1106 	if (ret != DDI_SUCCESS) {
1107 		DBG(NIUMX_DBG_A_INTX, dip, "i_ddi_add_ivintr error ret=%x\n",
1108 		    ret);
1109 		goto done;
1110 	}
1111 
1112 	/* select cpu, saving it for removal */
1113 	ih_p->ih_cpuid = intr_dist_cpuid();
1114 
1115 	if ((hvret = hvio_intr_settarget(ih_p->ih_sysino, ih_p->ih_cpuid))
1116 	    != H_EOK) {
1117 		DBG(NIUMX_DBG_A_INTX, dip,
1118 		    "hvio_intr_settarget failed, ret 0x%x\n", hvret);
1119 		ret = DDI_FAILURE;
1120 	}
1121 done:
1122 	DBG(NIUMX_DBG_A_INTX, dip, "done, ret = %d, ih_p 0x%p, hdlp 0x%p\n",
1123 	    ih_p, hdlp, ret);
1124 	return (ret);
1125 }
1126 
1127 /*
1128  * niumx_rem_intr:
1129  *
1130  * This function is called to unregister interrupts.
1131  */
1132 /*ARGSUSED*/
1133 int
niumx_rem_intr(dev_info_t * dip,dev_info_t * rdip,ddi_intr_handle_impl_t * hdlp)1134 niumx_rem_intr(dev_info_t *dip, dev_info_t *rdip,
1135     ddi_intr_handle_impl_t *hdlp)
1136 {
1137 	niumx_ih_t	*ih_p;
1138 	int		ret = DDI_SUCCESS, state;
1139 	hrtime_t	start;
1140 	niusysino_t 	sysino;
1141 	niumx_devstate_t	*niumxds_p;	/* devstate pointer */
1142 	int		instance = ddi_get_instance(dip);
1143 
1144 	niumxds_p = (niumx_devstate_t *)ddi_get_soft_state(niumx_state,
1145 	    instance);
1146 
1147 	ASSERT(hdlp->ih_inum < NIUMX_MAX_INTRS);
1148 
1149 	ih_p = niumxds_p->niumx_ihtable +  hdlp->ih_vector;
1150 
1151 	sysino = ih_p->ih_sysino;
1152 	DBG(NIUMX_DBG_R_INTX, dip, "removing (%x,%x)\n", ih_p->ih_inum, sysino);
1153 
1154 	(void) hvio_intr_setvalid(sysino, HV_INTR_NOTVALID);
1155 
1156 	/* check for pending interrupts, busy wait if so */
1157 	for (start = gethrtime(); !panicstr &&
1158 	    (hvio_intr_getstate(sysino, &state) == H_EOK) &&
1159 	    (state == HV_INTR_DELIVERED_STATE); /* */) {
1160 		if (gethrtime() - start > niumx_intr_timeout) {
1161 			cmn_err(CE_WARN, "%s%d: niumx_intr_dist: "
1162 			    "pending interrupt (%x,%lx) timedout\n",
1163 			    ddi_driver_name(dip), ddi_get_instance(dip),
1164 			    ih_p->ih_inum, sysino);
1165 			ret = DDI_FAILURE;
1166 			goto fail;
1167 		}
1168 	}
1169 
1170 	ih_p->ih_sysino = 0;
1171 
1172 	hdlp->ih_vector = (uint32_t)sysino;
1173 	if (hdlp->ih_vector !=  NULL) i_ddi_rem_ivintr(hdlp);
1174 
1175 fail:
1176 	return (ret);
1177 }
1178 
1179 /*
1180  * niumx_intr_hdlr (our interrupt handler)
1181  */
1182 uint_t
niumx_intr_hdlr(void * arg)1183 niumx_intr_hdlr(void *arg)
1184 {
1185 	niumx_ih_t *ih_p = (niumx_ih_t *)arg;
1186 	uint_t		r;
1187 
1188 	DTRACE_PROBE4(interrupt__start, dev_info_t, ih_p->ih_dip, void *,
1189 	    ih_p->ih_hdlr, caddr_t, ih_p->ih_arg1, caddr_t, ih_p->ih_arg2);
1190 
1191 	r = (*ih_p->ih_hdlr)(ih_p->ih_arg1, ih_p->ih_arg2);
1192 
1193 	DTRACE_PROBE4(interrupt__complete, dev_info_t, ih_p->ih_dip, void *,
1194 	    ih_p->ih_hdlr, caddr_t, ih_p->ih_arg1, int, r);
1195 
1196 	(void) hvio_intr_setstate(ih_p->ih_sysino, HV_INTR_IDLE_STATE);
1197 	return (r);
1198 }
1199 
1200 #ifdef	DEBUG
1201 uint64_t niumx_debug_flags = 0;
1202 
1203 static char *niumx_debug_sym [] = {	/* same sequence as niumx_debug_bit */
1204 	/*  0 */ "attach",
1205 	/*  1 */ "map",
1206 	/*  2 */ "nex-ctlops",
1207 	/*  3 */ "introps",
1208 	/*  4 */ "intr-add",
1209 	/*  5 */ "intr-rem",
1210 	/*  6 */ "intr",
1211 	/*  7 */ "dma-alloc",
1212 	/*  8 */ "dma-bind",
1213 	/*  9 */ "dma-unbind",
1214 	/* 10 */ "chk-dma-mode"
1215 };
1216 
1217 /*ARGSUSED*/
1218 void
niumx_dbg(niumx_debug_bit_t bit,dev_info_t * dip,char * fmt,...)1219 niumx_dbg(niumx_debug_bit_t bit, dev_info_t *dip, char *fmt, ...)
1220 {
1221 	va_list ap;
1222 	char msgbuf[1024];
1223 
1224 	if (!(1ull << bit & niumx_debug_flags))
1225 		return;
1226 	va_start(ap, fmt);
1227 	(void) vsprintf(msgbuf, fmt, ap);
1228 	va_end(ap);
1229 	cmn_err(CE_NOTE, "%s: %s", niumx_debug_sym[bit], msgbuf);
1230 }
1231 
1232 #endif	/* DEBUG */
1233