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