xref: /titanic_41/usr/src/uts/common/io/nxge/nxge_hio.c (revision c7402f0767d7a0360fabd0bd449c6baf9b282074)
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 /*
23  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*
28  * nxge_hio.c
29  *
30  * This file manages the virtualization resources for Neptune
31  * devices.  That is, it implements a hybrid I/O (HIO) approach in the
32  * Solaris kernel, whereby a guest domain on an LDOMs server may
33  * request & use hardware resources from the service domain.
34  *
35  */
36 
37 #include <sys/mac_provider.h>
38 #include <sys/nxge/nxge_impl.h>
39 #include <sys/nxge/nxge_fzc.h>
40 #include <sys/nxge/nxge_rxdma.h>
41 #include <sys/nxge/nxge_txdma.h>
42 #include <sys/nxge/nxge_hio.h>
43 
44 #define	NXGE_HIO_SHARE_MIN_CHANNELS 2
45 #define	NXGE_HIO_SHARE_MAX_CHANNELS 2
46 
47 /*
48  * External prototypes
49  */
50 extern npi_status_t npi_rxdma_dump_rdc_table(npi_handle_t, uint8_t);
51 
52 /* The following function may be found in nxge_main.c */
53 extern int nxge_m_mmac_remove(void *arg, int slot);
54 extern int nxge_m_mmac_add_g(void *arg, const uint8_t *maddr, int rdctbl,
55 	boolean_t usetbl);
56 
57 /* The following function may be found in nxge_[t|r]xdma.c */
58 extern npi_status_t nxge_txdma_channel_disable(nxge_t *, int);
59 extern nxge_status_t nxge_disable_rxdma_channel(nxge_t *, uint16_t);
60 
61 /*
62  * Local prototypes
63  */
64 static void nxge_grp_dc_append(nxge_t *, nxge_grp_t *, nxge_hio_dc_t *);
65 static nxge_hio_dc_t *nxge_grp_dc_unlink(nxge_t *, nxge_grp_t *, int);
66 static void nxge_grp_dc_map(nxge_grp_t *group);
67 
68 /*
69  * These functions are used by both service & guest domains to
70  * decide whether they're running in an LDOMs/XEN environment
71  * or not.  If so, then the Hybrid I/O (HIO) module is initialized.
72  */
73 
74 /*
75  * nxge_get_environs
76  *
77  *	Figure out if we are in a guest domain or not.
78  *
79  * Arguments:
80  * 	nxge
81  *
82  * Notes:
83  *
84  * Context:
85  *	Any domain
86  */
87 void
88 nxge_get_environs(
89 	nxge_t *nxge)
90 {
91 	char *string;
92 
93 	/*
94 	 * In the beginning, assume that we are running sans LDOMs/XEN.
95 	 */
96 	nxge->environs = SOLARIS_DOMAIN;
97 
98 	/*
99 	 * Are we a hybrid I/O (HIO) guest domain driver?
100 	 */
101 	if ((ddi_prop_lookup_string(DDI_DEV_T_ANY, nxge->dip,
102 	    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM,
103 	    "niutype", &string)) == DDI_PROP_SUCCESS) {
104 		if (strcmp(string, "n2niu") == 0) {
105 			nxge->environs = SOLARIS_GUEST_DOMAIN;
106 			/* So we can allocate properly-aligned memory. */
107 			nxge->niu_type = N2_NIU;
108 			NXGE_DEBUG_MSG((nxge, HIO_CTL,
109 			    "Hybrid IO-capable guest domain"));
110 		}
111 		ddi_prop_free(string);
112 	}
113 }
114 
115 #if !defined(sun4v)
116 
117 /*
118  * nxge_hio_init
119  *
120  *	Initialize the HIO module of the NXGE driver.
121  *
122  * Arguments:
123  * 	nxge
124  *
125  * Notes:
126  *	This is the non-hybrid I/O version of this function.
127  *
128  * Context:
129  *	Any domain
130  */
131 int
132 nxge_hio_init(nxge_t *nxge)
133 {
134 	nxge_hio_data_t *nhd;
135 	int i;
136 
137 	nhd = (nxge_hio_data_t *)nxge->nxge_hw_p->hio;
138 	if (nhd == 0) {
139 		nhd = KMEM_ZALLOC(sizeof (*nhd), KM_SLEEP);
140 		MUTEX_INIT(&nhd->lock, NULL, MUTEX_DRIVER, NULL);
141 		nxge->nxge_hw_p->hio = (uintptr_t)nhd;
142 	}
143 
144 	/*
145 	 * Initialize share and ring group structures.
146 	 */
147 	for (i = 0; i < NXGE_MAX_TDCS; i++)
148 		nxge->tdc_is_shared[i] = B_FALSE;
149 
150 	for (i = 0; i < NXGE_MAX_TDC_GROUPS; i++) {
151 		nxge->tx_hio_groups[i].ghandle = NULL;
152 		nxge->tx_hio_groups[i].nxgep = nxge;
153 		nxge->tx_hio_groups[i].type = MAC_RING_TYPE_TX;
154 		nxge->tx_hio_groups[i].gindex = 0;
155 		nxge->tx_hio_groups[i].sindex = 0;
156 	}
157 
158 	for (i = 0; i < NXGE_MAX_RDC_GROUPS; i++) {
159 		nxge->rx_hio_groups[i].ghandle = NULL;
160 		nxge->rx_hio_groups[i].nxgep = nxge;
161 		nxge->rx_hio_groups[i].type = MAC_RING_TYPE_RX;
162 		nxge->rx_hio_groups[i].gindex = 0;
163 		nxge->rx_hio_groups[i].sindex = 0;
164 		nxge->rx_hio_groups[i].started = B_FALSE;
165 		nxge->rx_hio_groups[i].port_default_grp = B_FALSE;
166 		nxge->rx_hio_groups[i].rdctbl = -1;
167 		nxge->rx_hio_groups[i].n_mac_addrs = 0;
168 	}
169 
170 	nhd->hio.ldoms = B_FALSE;
171 
172 	return (NXGE_OK);
173 }
174 
175 #endif
176 
177 void
178 nxge_hio_uninit(nxge_t *nxge)
179 {
180 	nxge_hio_data_t *nhd = (nxge_hio_data_t *)nxge->nxge_hw_p->hio;
181 
182 	ASSERT(nxge->nxge_hw_p->ndevs == 0);
183 
184 	if (nhd != NULL) {
185 		MUTEX_DESTROY(&nhd->lock);
186 		KMEM_FREE(nhd, sizeof (*nhd));
187 		nxge->nxge_hw_p->hio = 0;
188 	}
189 }
190 
191 /*
192  * nxge_dci_map
193  *
194  *	Map a DMA channel index to a channel number.
195  *
196  * Arguments:
197  * 	instance	The instance number of the driver.
198  * 	type		The type of channel this is: Tx or Rx.
199  * 	index		The index to convert to a channel number
200  *
201  * Notes:
202  *	This function is called by nxge_ndd.c:nxge_param_set_port_rdc()
203  *
204  * Context:
205  *	Any domain
206  */
207 int
208 nxge_dci_map(
209 	nxge_t *nxge,
210 	vpc_type_t type,
211 	int index)
212 {
213 	nxge_grp_set_t *set;
214 	int dc;
215 
216 	switch (type) {
217 	case VP_BOUND_TX:
218 		set = &nxge->tx_set;
219 		break;
220 	case VP_BOUND_RX:
221 		set = &nxge->rx_set;
222 		break;
223 	}
224 
225 	for (dc = 0; dc < NXGE_MAX_TDCS; dc++) {
226 		if ((1 << dc) & set->owned.map) {
227 			if (index == 0)
228 				return (dc);
229 			else
230 				index--;
231 		}
232 	}
233 
234 	return (-1);
235 }
236 
237 /*
238  * ---------------------------------------------------------------------
239  * These are the general-purpose DMA channel group functions.  That is,
240  * these functions are used to manage groups of TDCs or RDCs in an HIO
241  * environment.
242  *
243  * But is also expected that in the future they will be able to manage
244  * Crossbow groups.
245  * ---------------------------------------------------------------------
246  */
247 
248 /*
249  * nxge_grp_cleanup(p_nxge_t nxge)
250  *
251  *	Remove all outstanding groups.
252  *
253  * Arguments:
254  *	nxge
255  */
256 void
257 nxge_grp_cleanup(p_nxge_t nxge)
258 {
259 	nxge_grp_set_t *set;
260 	int i;
261 
262 	MUTEX_ENTER(&nxge->group_lock);
263 
264 	/*
265 	 * Find RX groups that need to be cleaned up.
266 	 */
267 	set = &nxge->rx_set;
268 	for (i = 0; i < NXGE_LOGICAL_GROUP_MAX; i++) {
269 		if (set->group[i] != NULL) {
270 			KMEM_FREE(set->group[i], sizeof (nxge_grp_t));
271 			set->group[i] = NULL;
272 		}
273 	}
274 
275 	/*
276 	 * Find TX groups that need to be cleaned up.
277 	 */
278 	set = &nxge->tx_set;
279 	for (i = 0; i < NXGE_LOGICAL_GROUP_MAX; i++) {
280 		if (set->group[i] != NULL) {
281 			KMEM_FREE(set->group[i], sizeof (nxge_grp_t));
282 			set->group[i] = NULL;
283 		}
284 	}
285 	MUTEX_EXIT(&nxge->group_lock);
286 }
287 
288 
289 /*
290  * nxge_grp_add
291  *
292  *	Add a group to an instance of NXGE.
293  *
294  * Arguments:
295  * 	nxge
296  * 	type	Tx or Rx
297  *
298  * Notes:
299  *
300  * Context:
301  *	Any domain
302  */
303 nxge_grp_t *
304 nxge_grp_add(
305 	nxge_t *nxge,
306 	nxge_grp_type_t type)
307 {
308 	nxge_grp_set_t *set;
309 	nxge_grp_t *group;
310 	int i;
311 
312 	group = KMEM_ZALLOC(sizeof (*group), KM_SLEEP);
313 	group->nxge = nxge;
314 
315 	MUTEX_ENTER(&nxge->group_lock);
316 	switch (type) {
317 	case NXGE_TRANSMIT_GROUP:
318 	case EXT_TRANSMIT_GROUP:
319 		set = &nxge->tx_set;
320 		break;
321 	default:
322 		set = &nxge->rx_set;
323 		break;
324 	}
325 
326 	group->type = type;
327 	group->active = B_TRUE;
328 	group->sequence = set->sequence++;
329 
330 	/* Find an empty slot for this logical group. */
331 	for (i = 0; i < NXGE_LOGICAL_GROUP_MAX; i++) {
332 		if (set->group[i] == 0) {
333 			group->index = i;
334 			set->group[i] = group;
335 			NXGE_DC_SET(set->lg.map, i);
336 			set->lg.count++;
337 			break;
338 		}
339 	}
340 	MUTEX_EXIT(&nxge->group_lock);
341 
342 	NXGE_DEBUG_MSG((nxge, HIO_CTL,
343 	    "nxge_grp_add: %cgroup = %d.%d",
344 	    type == NXGE_TRANSMIT_GROUP ? 't' : 'r',
345 	    nxge->mac.portnum, group->sequence));
346 
347 	return (group);
348 }
349 
350 void
351 nxge_grp_remove(
352 	nxge_t *nxge,
353 	nxge_grp_t *group)	/* The group to remove. */
354 {
355 	nxge_grp_set_t *set;
356 	vpc_type_t type;
357 
358 	MUTEX_ENTER(&nxge->group_lock);
359 	switch (group->type) {
360 	case NXGE_TRANSMIT_GROUP:
361 	case EXT_TRANSMIT_GROUP:
362 		set = &nxge->tx_set;
363 		break;
364 	default:
365 		set = &nxge->rx_set;
366 		break;
367 	}
368 
369 	if (set->group[group->index] != group) {
370 		MUTEX_EXIT(&nxge->group_lock);
371 		return;
372 	}
373 
374 	set->group[group->index] = 0;
375 	NXGE_DC_RESET(set->lg.map, group->index);
376 	set->lg.count--;
377 
378 	/* While inside the mutex, deactivate <group>. */
379 	group->active = B_FALSE;
380 
381 	MUTEX_EXIT(&nxge->group_lock);
382 
383 	NXGE_DEBUG_MSG((nxge, HIO_CTL,
384 	    "nxge_grp_remove(%c.%d.%d) called",
385 	    group->type == NXGE_TRANSMIT_GROUP ? 't' : 'r',
386 	    nxge->mac.portnum, group->sequence));
387 
388 	/* Now, remove any DCs which are still active. */
389 	switch (group->type) {
390 	default:
391 		type = VP_BOUND_TX;
392 		break;
393 	case NXGE_RECEIVE_GROUP:
394 	case EXT_RECEIVE_GROUP:
395 		type = VP_BOUND_RX;
396 	}
397 
398 	while (group->dc) {
399 		nxge_grp_dc_remove(nxge, type, group->dc->channel);
400 	}
401 
402 	KMEM_FREE(group, sizeof (*group));
403 }
404 
405 /*
406  * nxge_grp_dc_add
407  *
408  *	Add a DMA channel to a VR/Group.
409  *
410  * Arguments:
411  * 	nxge
412  * 	channel	The channel to add.
413  * Notes:
414  *
415  * Context:
416  *	Any domain
417  */
418 /* ARGSUSED */
419 int
420 nxge_grp_dc_add(
421 	nxge_t *nxge,
422 	nxge_grp_t *group,	/* The group to add <channel> to. */
423 	vpc_type_t type,	/* Rx or Tx */
424 	int channel)		/* A physical/logical channel number */
425 {
426 	nxge_hio_data_t *nhd = (nxge_hio_data_t *)nxge->nxge_hw_p->hio;
427 	nxge_hio_dc_t *dc;
428 	nxge_grp_set_t *set;
429 	nxge_status_t status = NXGE_OK;
430 
431 	NXGE_DEBUG_MSG((nxge, HIO_CTL, "==> nxge_grp_dc_add"));
432 
433 	if (group == 0)
434 		return (0);
435 
436 	switch (type) {
437 	case VP_BOUND_TX:
438 		set = &nxge->tx_set;
439 		if (channel > NXGE_MAX_TDCS) {
440 			NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
441 			    "nxge_grp_dc_add: TDC = %d", channel));
442 			return (NXGE_ERROR);
443 		}
444 		break;
445 	case VP_BOUND_RX:
446 		set = &nxge->rx_set;
447 		if (channel > NXGE_MAX_RDCS) {
448 			NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
449 			    "nxge_grp_dc_add: RDC = %d", channel));
450 			return (NXGE_ERROR);
451 		}
452 		break;
453 
454 	default:
455 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
456 		    "nxge_grp_dc_add: unknown type channel(%d)", channel));
457 	}
458 
459 	NXGE_DEBUG_MSG((nxge, HIO_CTL,
460 	    "nxge_grp_dc_add: %cgroup = %d.%d.%d, channel = %d",
461 	    type == VP_BOUND_TX ? 't' : 'r',
462 	    nxge->mac.portnum, group->sequence, group->count, channel));
463 
464 	MUTEX_ENTER(&nxge->group_lock);
465 	if (group->active != B_TRUE) {
466 		/* We may be in the process of removing this group. */
467 		MUTEX_EXIT(&nxge->group_lock);
468 		return (NXGE_ERROR);
469 	}
470 	MUTEX_EXIT(&nxge->group_lock);
471 
472 	if (!(dc = nxge_grp_dc_find(nxge, type, channel))) {
473 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
474 		    "nxge_grp_dc_add(%d): DC FIND failed", channel));
475 		return (NXGE_ERROR);
476 	}
477 
478 	MUTEX_ENTER(&nhd->lock);
479 
480 	if (dc->group) {
481 		MUTEX_EXIT(&nhd->lock);
482 		/* This channel is already in use! */
483 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
484 		    "nxge_grp_dc_add(%d): channel already in group", channel));
485 		return (NXGE_ERROR);
486 	}
487 
488 	dc->next = 0;
489 	dc->page = channel;
490 	dc->channel = (nxge_channel_t)channel;
491 
492 	dc->type = type;
493 	if (type == VP_BOUND_RX) {
494 		dc->init = nxge_init_rxdma_channel;
495 		dc->uninit = nxge_uninit_rxdma_channel;
496 	} else {
497 		dc->init = nxge_init_txdma_channel;
498 		dc->uninit = nxge_uninit_txdma_channel;
499 	}
500 
501 	dc->group = group;
502 
503 	if (isLDOMguest(nxge))
504 		(void) nxge_hio_ldsv_add(nxge, dc);
505 
506 	NXGE_DC_SET(set->owned.map, channel);
507 	set->owned.count++;
508 
509 	MUTEX_EXIT(&nhd->lock);
510 
511 	if ((status = (*dc->init)(nxge, channel)) != NXGE_OK) {
512 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
513 		    "nxge_grp_dc_add(%d): channel init failed", channel));
514 		MUTEX_ENTER(&nhd->lock);
515 		(void) memset(dc, 0, sizeof (*dc));
516 		NXGE_DC_RESET(set->owned.map, channel);
517 		set->owned.count--;
518 		MUTEX_EXIT(&nhd->lock);
519 		return (NXGE_ERROR);
520 	}
521 
522 	nxge_grp_dc_append(nxge, group, dc);
523 
524 	if (type == VP_BOUND_TX) {
525 		MUTEX_ENTER(&nhd->lock);
526 		nxge->tdc_is_shared[channel] = B_FALSE;
527 		MUTEX_EXIT(&nhd->lock);
528 	}
529 
530 	NXGE_DEBUG_MSG((nxge, HIO_CTL, "<== nxge_grp_dc_add"));
531 
532 	return ((int)status);
533 }
534 
535 void
536 nxge_grp_dc_remove(
537 	nxge_t *nxge,
538 	vpc_type_t type,
539 	int channel)
540 {
541 	nxge_hio_data_t *nhd = (nxge_hio_data_t *)nxge->nxge_hw_p->hio;
542 	nxge_hio_dc_t *dc;
543 	nxge_grp_set_t *set;
544 	nxge_grp_t *group;
545 
546 	dc_uninit_t uninit;
547 
548 	NXGE_DEBUG_MSG((nxge, HIO_CTL, "==> nxge_grp_dc_remove"));
549 
550 	if ((dc = nxge_grp_dc_find(nxge, type, channel)) == 0)
551 		goto nxge_grp_dc_remove_exit;
552 
553 	if ((dc->group == NULL) && (dc->next == 0) &&
554 	    (dc->channel == 0) && (dc->page == 0) && (dc->type == 0)) {
555 		goto nxge_grp_dc_remove_exit;
556 	}
557 
558 	group = (nxge_grp_t *)dc->group;
559 
560 	if (isLDOMguest(nxge)) {
561 		(void) nxge_hio_intr_remove(nxge, type, channel);
562 	}
563 
564 	NXGE_DEBUG_MSG((nxge, HIO_CTL,
565 	    "DC remove: group = %d.%d.%d, %cdc %d",
566 	    nxge->mac.portnum, group->sequence, group->count,
567 	    type == VP_BOUND_TX ? 't' : 'r', dc->channel));
568 
569 	MUTEX_ENTER(&nhd->lock);
570 
571 	set = dc->type == VP_BOUND_TX ? &nxge->tx_set : &nxge->rx_set;
572 
573 	/* Remove the DC from its group. */
574 	if (nxge_grp_dc_unlink(nxge, group, channel) != dc) {
575 		MUTEX_EXIT(&nhd->lock);
576 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
577 		    "nxge_grp_dc_remove(%d) failed", channel));
578 		goto nxge_grp_dc_remove_exit;
579 	}
580 
581 	uninit = dc->uninit;
582 	channel = dc->channel;
583 
584 	NXGE_DC_RESET(set->owned.map, channel);
585 	set->owned.count--;
586 
587 	(void) memset(dc, 0, sizeof (*dc));
588 
589 	MUTEX_EXIT(&nhd->lock);
590 
591 	(*uninit)(nxge, channel);
592 
593 nxge_grp_dc_remove_exit:
594 	NXGE_DEBUG_MSG((nxge, HIO_CTL, "<== nxge_grp_dc_remove"));
595 }
596 
597 nxge_hio_dc_t *
598 nxge_grp_dc_find(
599 	nxge_t *nxge,
600 	vpc_type_t type,	/* Rx or Tx */
601 	int channel)
602 {
603 	nxge_hio_data_t *nhd = (nxge_hio_data_t *)nxge->nxge_hw_p->hio;
604 	nxge_hio_dc_t *current;
605 
606 	current = (type == VP_BOUND_TX) ? &nhd->tdc[0] : &nhd->rdc[0];
607 
608 	if (!isLDOMguest(nxge)) {
609 		return (&current[channel]);
610 	} else {
611 		/* We're in a guest domain. */
612 		int i, limit = (type == VP_BOUND_TX) ?
613 		    NXGE_MAX_TDCS : NXGE_MAX_RDCS;
614 
615 		MUTEX_ENTER(&nhd->lock);
616 		for (i = 0; i < limit; i++, current++) {
617 			if (current->channel == channel) {
618 				if (current->vr && current->vr->nxge ==
619 				    (uintptr_t)nxge) {
620 					MUTEX_EXIT(&nhd->lock);
621 					return (current);
622 				}
623 			}
624 		}
625 		MUTEX_EXIT(&nhd->lock);
626 	}
627 
628 	return (0);
629 }
630 
631 /*
632  * nxge_grp_dc_append
633  *
634  *	Append a DMA channel to a group.
635  *
636  * Arguments:
637  * 	nxge
638  * 	group	The group to append to
639  * 	dc	The DMA channel to append
640  *
641  * Notes:
642  *
643  * Context:
644  *	Any domain
645  */
646 static
647 void
648 nxge_grp_dc_append(
649 	nxge_t *nxge,
650 	nxge_grp_t *group,
651 	nxge_hio_dc_t *dc)
652 {
653 	MUTEX_ENTER(&nxge->group_lock);
654 
655 	if (group->dc == 0) {
656 		group->dc = dc;
657 	} else {
658 		nxge_hio_dc_t *current = group->dc;
659 		do {
660 			if (current->next == 0) {
661 				current->next = dc;
662 				break;
663 			}
664 			current = current->next;
665 		} while (current);
666 	}
667 
668 	NXGE_DC_SET(group->map, dc->channel);
669 
670 	nxge_grp_dc_map(group);
671 	group->count++;
672 
673 	MUTEX_EXIT(&nxge->group_lock);
674 }
675 
676 /*
677  * nxge_grp_dc_unlink
678  *
679  *	Unlink a DMA channel fromits linked list (group).
680  *
681  * Arguments:
682  * 	nxge
683  * 	group	The group (linked list) to unlink from
684  * 	dc	The DMA channel to append
685  *
686  * Notes:
687  *
688  * Context:
689  *	Any domain
690  */
691 nxge_hio_dc_t *
692 nxge_grp_dc_unlink(
693 	nxge_t *nxge,
694 	nxge_grp_t *group,
695 	int channel)
696 {
697 	nxge_hio_dc_t *current, *previous;
698 
699 	MUTEX_ENTER(&nxge->group_lock);
700 
701 	if (group == NULL) {
702 		MUTEX_EXIT(&nxge->group_lock);
703 		return (0);
704 	}
705 
706 	if ((current = group->dc) == 0) {
707 		MUTEX_EXIT(&nxge->group_lock);
708 		return (0);
709 	}
710 
711 	previous = 0;
712 	do {
713 		if (current->channel == channel) {
714 			if (previous)
715 				previous->next = current->next;
716 			else
717 				group->dc = current->next;
718 			break;
719 		}
720 		previous = current;
721 		current = current->next;
722 	} while (current);
723 
724 	if (current == 0) {
725 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
726 		    "DC unlink: DC %d not found", channel));
727 	} else {
728 		current->next = 0;
729 		current->group = 0;
730 
731 		NXGE_DC_RESET(group->map, channel);
732 		group->count--;
733 	}
734 
735 	nxge_grp_dc_map(group);
736 
737 	MUTEX_EXIT(&nxge->group_lock);
738 
739 	return (current);
740 }
741 
742 /*
743  * nxge_grp_dc_map
744  *
745  *	Map a linked list to an array of channel numbers.
746  *
747  * Arguments:
748  * 	nxge
749  * 	group	The group to remap.
750  *
751  * Notes:
752  *	It is expected that the caller will hold the correct mutex.
753  *
754  * Context:
755  *	Service domain
756  */
757 void
758 nxge_grp_dc_map(
759 	nxge_grp_t *group)
760 {
761 	nxge_channel_t *legend;
762 	nxge_hio_dc_t *dc;
763 
764 	(void) memset(group->legend, 0, sizeof (group->legend));
765 
766 	legend = group->legend;
767 	dc = group->dc;
768 	while (dc) {
769 		*legend = dc->channel;
770 		legend++;
771 		dc = dc->next;
772 	}
773 }
774 
775 /*
776  * ---------------------------------------------------------------------
777  * These are HIO debugging functions.
778  * ---------------------------------------------------------------------
779  */
780 
781 /*
782  * nxge_delay
783  *
784  *	Delay <seconds> number of seconds.
785  *
786  * Arguments:
787  * 	nxge
788  * 	group	The group to append to
789  * 	dc	The DMA channel to append
790  *
791  * Notes:
792  *	This is a developer-only function.
793  *
794  * Context:
795  *	Any domain
796  */
797 void
798 nxge_delay(
799 	int seconds)
800 {
801 	delay(drv_usectohz(seconds * 1000000));
802 }
803 
804 static dmc_reg_name_t rx_names[] = {
805 	{ "RXDMA_CFIG1",	0 },
806 	{ "RXDMA_CFIG2",	8 },
807 	{ "RBR_CFIG_A",		0x10 },
808 	{ "RBR_CFIG_B",		0x18 },
809 	{ "RBR_KICK",		0x20 },
810 	{ "RBR_STAT",		0x28 },
811 	{ "RBR_HDH",		0x30 },
812 	{ "RBR_HDL",		0x38 },
813 	{ "RCRCFIG_A",		0x40 },
814 	{ "RCRCFIG_B",		0x48 },
815 	{ "RCRSTAT_A",		0x50 },
816 	{ "RCRSTAT_B",		0x58 },
817 	{ "RCRSTAT_C",		0x60 },
818 	{ "RX_DMA_ENT_MSK",	0x68 },
819 	{ "RX_DMA_CTL_STAT",	0x70 },
820 	{ "RCR_FLSH",		0x78 },
821 	{ "RXMISC",		0x90 },
822 	{ "RX_DMA_CTL_STAT_DBG", 0x98 },
823 	{ 0, -1 }
824 };
825 
826 static dmc_reg_name_t tx_names[] = {
827 	{ "Tx_RNG_CFIG",	0 },
828 	{ "Tx_RNG_HDL",		0x10 },
829 	{ "Tx_RNG_KICK",	0x18 },
830 	{ "Tx_ENT_MASK",	0x20 },
831 	{ "Tx_CS",		0x28 },
832 	{ "TxDMA_MBH",		0x30 },
833 	{ "TxDMA_MBL",		0x38 },
834 	{ "TxDMA_PRE_ST",	0x40 },
835 	{ "Tx_RNG_ERR_LOGH",	0x48 },
836 	{ "Tx_RNG_ERR_LOGL",	0x50 },
837 	{ "TDMC_INTR_DBG",	0x60 },
838 	{ "Tx_CS_DBG",		0x68 },
839 	{ 0, -1 }
840 };
841 
842 /*
843  * nxge_xx2str
844  *
845  *	Translate a register address into a string.
846  *
847  * Arguments:
848  * 	offset	The address of the register to translate.
849  *
850  * Notes:
851  *	These are developer-only function.
852  *
853  * Context:
854  *	Any domain
855  */
856 const char *
857 nxge_rx2str(
858 	int offset)
859 {
860 	dmc_reg_name_t *reg = &rx_names[0];
861 
862 	offset &= DMA_CSR_MASK;
863 
864 	while (reg->name) {
865 		if (offset == reg->offset)
866 			return (reg->name);
867 		reg++;
868 	}
869 
870 	return (0);
871 }
872 
873 const char *
874 nxge_tx2str(
875 	int offset)
876 {
877 	dmc_reg_name_t *reg = &tx_names[0];
878 
879 	offset &= DMA_CSR_MASK;
880 
881 	while (reg->name) {
882 		if (offset == reg->offset)
883 			return (reg->name);
884 		reg++;
885 	}
886 
887 	return (0);
888 }
889 
890 /*
891  * nxge_ddi_perror
892  *
893  *	Map a DDI error number to a string.
894  *
895  * Arguments:
896  * 	ddi_error	The DDI error number to map.
897  *
898  * Notes:
899  *
900  * Context:
901  *	Any domain
902  */
903 const char *
904 nxge_ddi_perror(
905 	int ddi_error)
906 {
907 	switch (ddi_error) {
908 	case DDI_SUCCESS:
909 		return ("DDI_SUCCESS");
910 	case DDI_FAILURE:
911 		return ("DDI_FAILURE");
912 	case DDI_NOT_WELL_FORMED:
913 		return ("DDI_NOT_WELL_FORMED");
914 	case DDI_EAGAIN:
915 		return ("DDI_EAGAIN");
916 	case DDI_EINVAL:
917 		return ("DDI_EINVAL");
918 	case DDI_ENOTSUP:
919 		return ("DDI_ENOTSUP");
920 	case DDI_EPENDING:
921 		return ("DDI_EPENDING");
922 	case DDI_ENOMEM:
923 		return ("DDI_ENOMEM");
924 	case DDI_EBUSY:
925 		return ("DDI_EBUSY");
926 	case DDI_ETRANSPORT:
927 		return ("DDI_ETRANSPORT");
928 	case DDI_ECONTEXT:
929 		return ("DDI_ECONTEXT");
930 	default:
931 		return ("Unknown error");
932 	}
933 }
934 
935 /*
936  * ---------------------------------------------------------------------
937  * These are Sun4v HIO function definitions
938  * ---------------------------------------------------------------------
939  */
940 
941 #if defined(sun4v)
942 
943 /*
944  * Local prototypes
945  */
946 static nxge_hio_vr_t *nxge_hio_vr_share(nxge_t *);
947 static void nxge_hio_unshare(nxge_hio_vr_t *);
948 
949 static int nxge_hio_addres(nxge_hio_vr_t *, mac_ring_type_t, uint64_t *);
950 static void nxge_hio_remres(nxge_hio_vr_t *, mac_ring_type_t, res_map_t);
951 
952 static void nxge_hio_tdc_unshare(nxge_t *nxge, int dev_grpid, int channel);
953 static void nxge_hio_rdc_unshare(nxge_t *nxge, int dev_grpid, int channel);
954 static int nxge_hio_dc_share(nxge_t *, nxge_hio_vr_t *, mac_ring_type_t, int);
955 static void nxge_hio_dc_unshare(nxge_t *, nxge_hio_vr_t *,
956     mac_ring_type_t, int);
957 
958 /*
959  * nxge_hio_init
960  *
961  *	Initialize the HIO module of the NXGE driver.
962  *
963  * Arguments:
964  * 	nxge
965  *
966  * Notes:
967  *
968  * Context:
969  *	Any domain
970  */
971 int
972 nxge_hio_init(
973 	nxge_t *nxge)
974 {
975 	nxge_hio_data_t *nhd;
976 	int i, region;
977 
978 	nhd = (nxge_hio_data_t *)nxge->nxge_hw_p->hio;
979 	if (nhd == 0) {
980 		nhd = KMEM_ZALLOC(sizeof (*nhd), KM_SLEEP);
981 		MUTEX_INIT(&nhd->lock, NULL, MUTEX_DRIVER, NULL);
982 		nxge->nxge_hw_p->hio = (uintptr_t)nhd;
983 	}
984 
985 	if ((nxge->environs == SOLARIS_DOMAIN) &&
986 	    (nxge->niu_type == N2_NIU)) {
987 		if (nxge->niu_hsvc_available == B_TRUE) {
988 			hsvc_info_t *niu_hsvc = &nxge->niu_hsvc;
989 			if (niu_hsvc->hsvc_major == 1 &&
990 			    niu_hsvc->hsvc_minor == 1)
991 				nxge->environs = SOLARIS_SERVICE_DOMAIN;
992 			NXGE_DEBUG_MSG((nxge, HIO_CTL,
993 			    "nxge_hio_init: hypervisor services "
994 			    "version %d.%d",
995 			    niu_hsvc->hsvc_major, niu_hsvc->hsvc_minor));
996 		}
997 	}
998 
999 	/*
1000 	 * Initialize share and ring group structures.
1001 	 */
1002 	for (i = 0; i < NXGE_MAX_TDC_GROUPS; i++) {
1003 		nxge->tx_hio_groups[i].ghandle = NULL;
1004 		nxge->tx_hio_groups[i].nxgep = nxge;
1005 		nxge->tx_hio_groups[i].type = MAC_RING_TYPE_TX;
1006 		nxge->tx_hio_groups[i].gindex = 0;
1007 		nxge->tx_hio_groups[i].sindex = 0;
1008 	}
1009 
1010 	for (i = 0; i < NXGE_MAX_RDC_GROUPS; i++) {
1011 		nxge->rx_hio_groups[i].ghandle = NULL;
1012 		nxge->rx_hio_groups[i].nxgep = nxge;
1013 		nxge->rx_hio_groups[i].type = MAC_RING_TYPE_RX;
1014 		nxge->rx_hio_groups[i].gindex = 0;
1015 		nxge->rx_hio_groups[i].sindex = 0;
1016 		nxge->rx_hio_groups[i].started = B_FALSE;
1017 		nxge->rx_hio_groups[i].port_default_grp = B_FALSE;
1018 		nxge->rx_hio_groups[i].rdctbl = -1;
1019 		nxge->rx_hio_groups[i].n_mac_addrs = 0;
1020 	}
1021 
1022 	if (!isLDOMs(nxge)) {
1023 		nhd->hio.ldoms = B_FALSE;
1024 		return (NXGE_OK);
1025 	}
1026 
1027 	nhd->hio.ldoms = B_TRUE;
1028 
1029 	/*
1030 	 * Fill in what we can.
1031 	 */
1032 	for (region = 0; region < NXGE_VR_SR_MAX; region++) {
1033 		nhd->vr[region].region = region;
1034 	}
1035 	nhd->vrs = NXGE_VR_SR_MAX - 2;
1036 
1037 	/*
1038 	 * Initialize the share stuctures.
1039 	 */
1040 	for (i = 0; i < NXGE_MAX_TDCS; i++)
1041 		nxge->tdc_is_shared[i] = B_FALSE;
1042 
1043 	for (i = 0; i < NXGE_VR_SR_MAX; i++) {
1044 		nxge->shares[i].nxgep = nxge;
1045 		nxge->shares[i].index = 0;
1046 		nxge->shares[i].vrp = NULL;
1047 		nxge->shares[i].tmap = 0;
1048 		nxge->shares[i].rmap = 0;
1049 		nxge->shares[i].rxgroup = 0;
1050 		nxge->shares[i].active = B_FALSE;
1051 	}
1052 
1053 	/* Fill in the HV HIO function pointers. */
1054 	nxge_hio_hv_init(nxge);
1055 
1056 	if (isLDOMservice(nxge)) {
1057 		NXGE_DEBUG_MSG((nxge, HIO_CTL,
1058 		    "Hybrid IO-capable service domain"));
1059 		return (NXGE_OK);
1060 	} else {
1061 		/*
1062 		 * isLDOMguest(nxge) == B_TRUE
1063 		 */
1064 		nx_vio_fp_t *vio;
1065 		nhd->type = NXGE_HIO_TYPE_GUEST;
1066 
1067 		vio = &nhd->hio.vio;
1068 		vio->__register = (vio_net_resource_reg_t)
1069 		    modgetsymvalue("vio_net_resource_reg", 0);
1070 		vio->unregister = (vio_net_resource_unreg_t)
1071 		    modgetsymvalue("vio_net_resource_unreg", 0);
1072 
1073 		if (vio->__register == 0 || vio->unregister == 0) {
1074 			NXGE_ERROR_MSG((nxge, VIR_CTL, "vio_net is absent!"));
1075 			return (NXGE_ERROR);
1076 		}
1077 	}
1078 
1079 	return (0);
1080 }
1081 #endif /* defined(sun4v) */
1082 
1083 static int
1084 nxge_hio_group_mac_add(nxge_t *nxge, nxge_ring_group_t *g,
1085     const uint8_t *macaddr)
1086 {
1087 	int rv;
1088 	nxge_rdc_grp_t *group;
1089 
1090 	mutex_enter(nxge->genlock);
1091 
1092 	/*
1093 	 * Initialize the NXGE RDC table data structure.
1094 	 */
1095 	group = &nxge->pt_config.rdc_grps[g->rdctbl];
1096 	if (!group->flag) {
1097 		group->port = NXGE_GET_PORT_NUM(nxge->function_num);
1098 		group->config_method = RDC_TABLE_ENTRY_METHOD_REP;
1099 		group->flag = B_TRUE;	/* This group has been configured. */
1100 	}
1101 
1102 	mutex_exit(nxge->genlock);
1103 
1104 	/*
1105 	 * Add the MAC address.
1106 	 */
1107 	if ((rv = nxge_m_mmac_add_g((void *)nxge, macaddr,
1108 	    g->rdctbl, B_TRUE)) != 0) {
1109 		return (rv);
1110 	}
1111 
1112 	mutex_enter(nxge->genlock);
1113 	g->n_mac_addrs++;
1114 	mutex_exit(nxge->genlock);
1115 	return (0);
1116 }
1117 
1118 static int
1119 nxge_hio_set_unicst(void *arg, const uint8_t *macaddr)
1120 {
1121 	p_nxge_t		nxgep = (p_nxge_t)arg;
1122 	struct ether_addr	addrp;
1123 
1124 	bcopy(macaddr, (uint8_t *)&addrp, ETHERADDRL);
1125 	if (nxge_set_mac_addr(nxgep, &addrp)) {
1126 		NXGE_ERROR_MSG((nxgep, NXGE_ERR_CTL,
1127 		    "<== nxge_m_unicst: set unitcast failed"));
1128 		return (EINVAL);
1129 	}
1130 
1131 	nxgep->primary = B_TRUE;
1132 
1133 	return (0);
1134 }
1135 
1136 /*ARGSUSED*/
1137 static int
1138 nxge_hio_clear_unicst(p_nxge_t nxgep, const uint8_t *mac_addr)
1139 {
1140 	nxgep->primary = B_FALSE;
1141 	return (0);
1142 }
1143 
1144 static int
1145 nxge_hio_add_mac(void *arg, const uint8_t *mac_addr)
1146 {
1147 	nxge_ring_group_t *group = (nxge_ring_group_t *)arg;
1148 	p_nxge_t nxge = group->nxgep;
1149 	int rv;
1150 	nxge_hio_vr_t *vr;	/* The Virtualization Region */
1151 
1152 	ASSERT(group->type == MAC_RING_TYPE_RX);
1153 
1154 	mutex_enter(nxge->genlock);
1155 
1156 	if (!nxge->primary && group->port_default_grp) {
1157 		rv = nxge_hio_set_unicst((void *)nxge, mac_addr);
1158 		mutex_exit(nxge->genlock);
1159 		return (rv);
1160 	}
1161 
1162 	/*
1163 	 * If the group is associated with a VR, then only one
1164 	 * address may be assigned to the group.
1165 	 */
1166 	vr = (nxge_hio_vr_t *)nxge->shares[group->sindex].vrp;
1167 	if ((vr != NULL) && (group->n_mac_addrs)) {
1168 		mutex_exit(nxge->genlock);
1169 		return (ENOSPC);
1170 	}
1171 
1172 	mutex_exit(nxge->genlock);
1173 
1174 	/*
1175 	 * Program the mac address for the group.
1176 	 */
1177 	if ((rv = nxge_hio_group_mac_add(nxge, group,
1178 	    mac_addr)) != 0) {
1179 		return (rv);
1180 	}
1181 
1182 	return (0);
1183 }
1184 
1185 static int
1186 find_mac_slot(nxge_mmac_t *mmac_info, const uint8_t *mac_addr)
1187 {
1188 	int i;
1189 	for (i = 0; i <= mmac_info->num_mmac; i++) {
1190 		if (memcmp(mmac_info->mac_pool[i].addr, mac_addr,
1191 		    ETHERADDRL) == 0) {
1192 			return (i);
1193 		}
1194 	}
1195 	return (-1);
1196 }
1197 
1198 /* ARGSUSED */
1199 static int
1200 nxge_hio_rem_mac(void *arg, const uint8_t *mac_addr)
1201 {
1202 	nxge_ring_group_t *group = (nxge_ring_group_t *)arg;
1203 	struct ether_addr addrp;
1204 	p_nxge_t nxge = group->nxgep;
1205 	nxge_mmac_t *mmac_info;
1206 	int rv, slot;
1207 
1208 	ASSERT(group->type == MAC_RING_TYPE_RX);
1209 
1210 	mutex_enter(nxge->genlock);
1211 
1212 	mmac_info = &nxge->nxge_mmac_info;
1213 	slot = find_mac_slot(mmac_info, mac_addr);
1214 	if (slot < 0) {
1215 		if (group->port_default_grp && nxge->primary) {
1216 			bcopy(mac_addr, (uint8_t *)&addrp, ETHERADDRL);
1217 			if (ether_cmp(&addrp, &nxge->ouraddr) == 0) {
1218 				rv = nxge_hio_clear_unicst(nxge, mac_addr);
1219 				mutex_exit(nxge->genlock);
1220 				return (rv);
1221 			} else {
1222 				mutex_exit(nxge->genlock);
1223 				return (EINVAL);
1224 			}
1225 		} else {
1226 			mutex_exit(nxge->genlock);
1227 			return (EINVAL);
1228 		}
1229 	}
1230 
1231 	mutex_exit(nxge->genlock);
1232 
1233 	/*
1234 	 * Remove the mac address for the group
1235 	 */
1236 	if ((rv = nxge_m_mmac_remove(nxge, slot)) != 0) {
1237 		return (rv);
1238 	}
1239 
1240 	mutex_enter(nxge->genlock);
1241 	group->n_mac_addrs--;
1242 	mutex_exit(nxge->genlock);
1243 
1244 	return (0);
1245 }
1246 
1247 static int
1248 nxge_hio_group_start(mac_group_driver_t gdriver)
1249 {
1250 	nxge_ring_group_t	*group = (nxge_ring_group_t *)gdriver;
1251 	nxge_rdc_grp_t		*rdc_grp_p;
1252 	int			rdctbl;
1253 	int			dev_gindex;
1254 
1255 	ASSERT(group->type == MAC_RING_TYPE_RX);
1256 
1257 #ifdef later
1258 	ASSERT(group->nxgep->nxge_mac_state == NXGE_MAC_STARTED);
1259 #endif
1260 	if (group->nxgep->nxge_mac_state != NXGE_MAC_STARTED)
1261 		return (ENXIO);
1262 
1263 	mutex_enter(group->nxgep->genlock);
1264 	dev_gindex = group->nxgep->pt_config.hw_config.def_mac_rxdma_grpid +
1265 	    group->gindex;
1266 	rdc_grp_p = &group->nxgep->pt_config.rdc_grps[dev_gindex];
1267 
1268 	/*
1269 	 * Get an rdc table for this group.
1270 	 * Group ID is given by the caller, and that's the group it needs
1271 	 * to bind to.  The default group is already bound when the driver
1272 	 * was attached.
1273 	 *
1274 	 * For Group 0, it's RDC table was allocated at attach time
1275 	 * no need to allocate a new table.
1276 	 */
1277 	if (group->gindex != 0) {
1278 		rdctbl = nxge_fzc_rdc_tbl_bind(group->nxgep,
1279 		    dev_gindex, B_TRUE);
1280 		if (rdctbl < 0) {
1281 			mutex_exit(group->nxgep->genlock);
1282 			return (rdctbl);
1283 		}
1284 	} else {
1285 		rdctbl = group->nxgep->pt_config.hw_config.def_mac_rxdma_grpid;
1286 	}
1287 
1288 	group->rdctbl = rdctbl;
1289 
1290 	(void) nxge_init_fzc_rdc_tbl(group->nxgep, rdc_grp_p, rdctbl);
1291 
1292 	group->started = B_TRUE;
1293 	mutex_exit(group->nxgep->genlock);
1294 
1295 	return (0);
1296 }
1297 
1298 static void
1299 nxge_hio_group_stop(mac_group_driver_t gdriver)
1300 {
1301 	nxge_ring_group_t *group = (nxge_ring_group_t *)gdriver;
1302 
1303 	ASSERT(group->type == MAC_RING_TYPE_RX);
1304 
1305 	mutex_enter(group->nxgep->genlock);
1306 	group->started = B_FALSE;
1307 
1308 	/*
1309 	 * Unbind the RDC table previously bound for this group.
1310 	 *
1311 	 * Since RDC table for group 0 was allocated at attach
1312 	 * time, no need to unbind the table here.
1313 	 */
1314 	if (group->gindex != 0)
1315 		(void) nxge_fzc_rdc_tbl_unbind(group->nxgep, group->rdctbl);
1316 
1317 	mutex_exit(group->nxgep->genlock);
1318 }
1319 
1320 /* ARGSUSED */
1321 void
1322 nxge_hio_group_get(void *arg, mac_ring_type_t type, int groupid,
1323 	mac_group_info_t *infop, mac_group_handle_t ghdl)
1324 {
1325 	p_nxge_t		nxgep = (p_nxge_t)arg;
1326 	nxge_ring_group_t	*group;
1327 	int			dev_gindex;
1328 
1329 	switch (type) {
1330 	case MAC_RING_TYPE_RX:
1331 		group = &nxgep->rx_hio_groups[groupid];
1332 		group->nxgep = nxgep;
1333 		group->ghandle = ghdl;
1334 		group->gindex = groupid;
1335 		group->sindex = 0;	/* not yet bound to a share */
1336 
1337 		dev_gindex = nxgep->pt_config.hw_config.def_mac_rxdma_grpid +
1338 		    groupid;
1339 
1340 		if (nxgep->pt_config.hw_config.def_mac_rxdma_grpid ==
1341 		    dev_gindex)
1342 			group->port_default_grp = B_TRUE;
1343 
1344 		infop->mgi_driver = (mac_group_driver_t)group;
1345 		infop->mgi_start = nxge_hio_group_start;
1346 		infop->mgi_stop = nxge_hio_group_stop;
1347 		infop->mgi_addmac = nxge_hio_add_mac;
1348 		infop->mgi_remmac = nxge_hio_rem_mac;
1349 		infop->mgi_count =
1350 		    nxgep->pt_config.rdc_grps[dev_gindex].max_rdcs;
1351 		break;
1352 
1353 	case MAC_RING_TYPE_TX:
1354 		/*
1355 		 * 'groupid' for TX should be incremented by one since
1356 		 * the default group (groupid 0) is not known by the MAC layer
1357 		 */
1358 		group = &nxgep->tx_hio_groups[groupid + 1];
1359 		group->nxgep = nxgep;
1360 		group->ghandle = ghdl;
1361 		group->gindex = groupid + 1;
1362 		group->sindex = 0;	/* not yet bound to a share */
1363 
1364 		infop->mgi_driver = (mac_group_driver_t)group;
1365 		infop->mgi_start = NULL;
1366 		infop->mgi_stop = NULL;
1367 		infop->mgi_addmac = NULL;	/* not needed */
1368 		infop->mgi_remmac = NULL;	/* not needed */
1369 		/* no rings associated with group initially */
1370 		infop->mgi_count = 0;
1371 		break;
1372 	}
1373 }
1374 
1375 #if defined(sun4v)
1376 
1377 int
1378 nxge_hio_share_assign(
1379 	nxge_t *nxge,
1380 	uint64_t cookie,
1381 	res_map_t *tmap,
1382 	res_map_t *rmap,
1383 	nxge_hio_vr_t *vr)
1384 {
1385 	nxge_hio_data_t *nhd = (nxge_hio_data_t *)nxge->nxge_hw_p->hio;
1386 	uint64_t slot, hv_rv;
1387 	nxge_hio_dc_t *dc;
1388 	nxhv_vr_fp_t *fp;
1389 	int i;
1390 
1391 	/*
1392 	 * Ask the Hypervisor to set up the VR for us
1393 	 */
1394 	fp = &nhd->hio.vr;
1395 	if ((hv_rv = (*fp->assign)(vr->region, cookie, &vr->cookie))) {
1396 		NXGE_ERROR_MSG((nxge, HIO_CTL,
1397 		    "nxge_hio_share_assign: "
1398 		    "vr->assign() returned %d", hv_rv));
1399 		return (-EIO);
1400 	}
1401 
1402 	/*
1403 	 * For each shared TDC, ask the HV to find us an empty slot.
1404 	 * -----------------------------------------------------
1405 	 */
1406 	dc = vr->tx_group.dc;
1407 	for (i = 0; i < NXGE_MAX_TDCS; i++) {
1408 		nxhv_dc_fp_t *tx = &nhd->hio.tx;
1409 		while (dc) {
1410 			hv_rv = (*tx->assign)
1411 			    (vr->cookie, dc->channel, &slot);
1412 			if (hv_rv != 0) {
1413 				NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
1414 				    "nxge_hio_share_assign: "
1415 				    "tx->assign(%x, %d) failed: %ld",
1416 				    vr->cookie, dc->channel, hv_rv));
1417 				return (-EIO);
1418 			}
1419 
1420 			dc->cookie = vr->cookie;
1421 			dc->page = (vp_channel_t)slot;
1422 
1423 			/* Inform the caller about the slot chosen. */
1424 			(*tmap) |= 1 << slot;
1425 
1426 			dc = dc->next;
1427 		}
1428 	}
1429 
1430 	/*
1431 	 * For each shared RDC, ask the HV to find us an empty slot.
1432 	 * -----------------------------------------------------
1433 	 */
1434 	dc = vr->rx_group.dc;
1435 	for (i = 0; i < NXGE_MAX_RDCS; i++) {
1436 		nxhv_dc_fp_t *rx = &nhd->hio.rx;
1437 		while (dc) {
1438 			hv_rv = (*rx->assign)
1439 			    (vr->cookie, dc->channel, &slot);
1440 			if (hv_rv != 0) {
1441 				NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
1442 				    "nxge_hio_share_assign: "
1443 				    "rx->assign(%x, %d) failed: %ld",
1444 				    vr->cookie, dc->channel, hv_rv));
1445 				return (-EIO);
1446 			}
1447 
1448 			dc->cookie = vr->cookie;
1449 			dc->page = (vp_channel_t)slot;
1450 
1451 			/* Inform the caller about the slot chosen. */
1452 			(*rmap) |= 1 << slot;
1453 
1454 			dc = dc->next;
1455 		}
1456 	}
1457 
1458 	return (0);
1459 }
1460 
1461 void
1462 nxge_hio_share_unassign(
1463 	nxge_hio_vr_t *vr)
1464 {
1465 	nxge_t *nxge = (nxge_t *)vr->nxge;
1466 	nxge_hio_data_t *nhd;
1467 	nxge_hio_dc_t *dc;
1468 	nxhv_vr_fp_t *fp;
1469 	uint64_t hv_rv;
1470 
1471 	nhd = (nxge_hio_data_t *)nxge->nxge_hw_p->hio;
1472 
1473 	dc = vr->tx_group.dc;
1474 	while (dc) {
1475 		nxhv_dc_fp_t *tx = &nhd->hio.tx;
1476 		hv_rv = (*tx->unassign)(vr->cookie, dc->page);
1477 		if (hv_rv != 0) {
1478 			NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
1479 			    "nxge_hio_share_unassign: "
1480 			    "tx->unassign(%x, %d) failed: %ld",
1481 			    vr->cookie, dc->page, hv_rv));
1482 		}
1483 		dc = dc->next;
1484 	}
1485 
1486 	dc = vr->rx_group.dc;
1487 	while (dc) {
1488 		nxhv_dc_fp_t *rx = &nhd->hio.rx;
1489 		hv_rv = (*rx->unassign)(vr->cookie, dc->page);
1490 		if (hv_rv != 0) {
1491 			NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
1492 			    "nxge_hio_share_unassign: "
1493 			    "rx->unassign(%x, %d) failed: %ld",
1494 			    vr->cookie, dc->page, hv_rv));
1495 		}
1496 		dc = dc->next;
1497 	}
1498 
1499 	fp = &nhd->hio.vr;
1500 	if (fp->unassign) {
1501 		hv_rv = (*fp->unassign)(vr->cookie);
1502 		if (hv_rv != 0) {
1503 			NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
1504 			    "nxge_hio_share_unassign: "
1505 			    "vr->assign(%x) failed: %ld",
1506 			    vr->cookie, hv_rv));
1507 		}
1508 	}
1509 }
1510 
1511 int
1512 nxge_hio_share_alloc(void *arg, mac_share_handle_t *shandle)
1513 {
1514 	p_nxge_t		nxge = (p_nxge_t)arg;
1515 	nxge_share_handle_t	*shp;
1516 	nxge_hio_vr_t		*vr;	/* The Virtualization Region */
1517 	nxge_hio_data_t		*nhd = (nxge_hio_data_t *)nxge->nxge_hw_p->hio;
1518 
1519 	NXGE_DEBUG_MSG((nxge, HIO_CTL, "==> nxge_hio_share"));
1520 
1521 	if (nhd->hio.vr.assign == 0 || nhd->hio.tx.assign == 0 ||
1522 	    nhd->hio.rx.assign == 0) {
1523 		NXGE_ERROR_MSG((nxge, HIO_CTL, "HV assign function(s) NULL"));
1524 		return (EIO);
1525 	}
1526 
1527 	/*
1528 	 * Get a VR.
1529 	 */
1530 	if ((vr = nxge_hio_vr_share(nxge)) == 0)
1531 		return (EAGAIN);
1532 
1533 	shp = &nxge->shares[vr->region];
1534 	shp->nxgep = nxge;
1535 	shp->index = vr->region;
1536 	shp->vrp = (void *)vr;
1537 	shp->tmap = shp->rmap = 0;	/* to be assigned by ms_sbind */
1538 	shp->rxgroup = 0;		/* to be assigned by ms_sadd */
1539 	shp->active = B_FALSE;		/* not bound yet */
1540 
1541 	*shandle = (mac_share_handle_t)shp;
1542 
1543 	NXGE_DEBUG_MSG((nxge, HIO_CTL, "<== nxge_hio_share"));
1544 	return (0);
1545 }
1546 
1547 
1548 void
1549 nxge_hio_share_free(mac_share_handle_t shandle)
1550 {
1551 	nxge_share_handle_t	*shp = (nxge_share_handle_t *)shandle;
1552 	nxge_hio_vr_t		*vr;
1553 
1554 	/*
1555 	 * Clear internal handle state.
1556 	 */
1557 	vr = shp->vrp;
1558 	shp->vrp = (void *)NULL;
1559 	shp->index = 0;
1560 	shp->tmap = 0;
1561 	shp->rmap = 0;
1562 	shp->rxgroup = 0;
1563 	shp->active = B_FALSE;
1564 
1565 	/*
1566 	 * Free VR resource.
1567 	 */
1568 	nxge_hio_unshare(vr);
1569 }
1570 
1571 
1572 void
1573 nxge_hio_share_query(mac_share_handle_t shandle, mac_ring_type_t type,
1574     mac_ring_handle_t *rings, uint_t *n_rings)
1575 {
1576 	nxge_t			*nxge;
1577 	nxge_share_handle_t	*shp = (nxge_share_handle_t *)shandle;
1578 	nxge_ring_handle_t	*rh;
1579 	uint32_t		offset;
1580 
1581 	nxge = shp->nxgep;
1582 
1583 	switch (type) {
1584 	case MAC_RING_TYPE_RX:
1585 		rh = nxge->rx_ring_handles;
1586 		offset = nxge->pt_config.hw_config.start_rdc;
1587 		break;
1588 
1589 	case MAC_RING_TYPE_TX:
1590 		rh = nxge->tx_ring_handles;
1591 		offset = nxge->pt_config.hw_config.tdc.start;
1592 		break;
1593 	}
1594 
1595 	/*
1596 	 * In version 1.0, we may only give a VR 2 RDCs/TDCs.  Not only that,
1597 	 * but the HV has statically assigned the channels like so:
1598 	 * VR0: RDC0 & RDC1
1599 	 * VR1: RDC2 & RDC3, etc.
1600 	 * The TDCs are assigned in exactly the same way.
1601 	 */
1602 	if (rings != NULL) {
1603 		rings[0] = rh[(shp->index * 2) - offset].ring_handle;
1604 		rings[1] = rh[(shp->index * 2 + 1) - offset].ring_handle;
1605 	}
1606 	if (n_rings != NULL) {
1607 		*n_rings = 2;
1608 	}
1609 }
1610 
1611 int
1612 nxge_hio_share_add_group(mac_share_handle_t shandle,
1613     mac_group_driver_t ghandle)
1614 {
1615 	nxge_t			*nxge;
1616 	nxge_share_handle_t	*shp = (nxge_share_handle_t *)shandle;
1617 	nxge_ring_group_t	*rg = (nxge_ring_group_t *)ghandle;
1618 	nxge_hio_vr_t		*vr;	/* The Virtualization Region */
1619 	nxge_grp_t		*group;
1620 	int			i;
1621 
1622 	if (rg->sindex != 0) {
1623 		/* the group is already bound to a share */
1624 		return (EALREADY);
1625 	}
1626 
1627 	/*
1628 	 * If we are adding a group 0 to a share, this
1629 	 * is not correct.
1630 	 */
1631 	ASSERT(rg->gindex != 0);
1632 
1633 	nxge = rg->nxgep;
1634 	vr = shp->vrp;
1635 
1636 	switch (rg->type) {
1637 	case MAC_RING_TYPE_RX:
1638 		/*
1639 		 * Make sure that the group has the right rings associated
1640 		 * for the share. In version 1.0, we may only give a VR
1641 		 * 2 RDCs.  Not only that, but the HV has statically
1642 		 * assigned the channels like so:
1643 		 * VR0: RDC0 & RDC1
1644 		 * VR1: RDC2 & RDC3, etc.
1645 		 */
1646 		group = nxge->rx_set.group[rg->gindex];
1647 
1648 		if (group->count > 2) {
1649 			/* a share can have at most 2 rings */
1650 			return (EINVAL);
1651 		}
1652 
1653 		for (i = 0; i < NXGE_MAX_RDCS; i++) {
1654 			if (group->map & (1 << i)) {
1655 				if ((i != shp->index * 2) &&
1656 				    (i != (shp->index * 2 + 1))) {
1657 					/*
1658 					 * A group with invalid rings was
1659 					 * attempted to bind to this share
1660 					 */
1661 					return (EINVAL);
1662 				}
1663 			}
1664 		}
1665 
1666 		rg->sindex = vr->region;
1667 		vr->rdc_tbl = rg->rdctbl;
1668 		shp->rxgroup = vr->rdc_tbl;
1669 		break;
1670 
1671 	case MAC_RING_TYPE_TX:
1672 		/*
1673 		 * Make sure that the group has the right rings associated
1674 		 * for the share. In version 1.0, we may only give a VR
1675 		 * 2 TDCs.  Not only that, but the HV has statically
1676 		 * assigned the channels like so:
1677 		 * VR0: TDC0 & TDC1
1678 		 * VR1: TDC2 & TDC3, etc.
1679 		 */
1680 		group = nxge->tx_set.group[rg->gindex];
1681 
1682 		if (group->count > 2) {
1683 			/* a share can have at most 2 rings */
1684 			return (EINVAL);
1685 		}
1686 
1687 		for (i = 0; i < NXGE_MAX_TDCS; i++) {
1688 			if (group->map & (1 << i)) {
1689 				if ((i != shp->index * 2) &&
1690 				    (i != (shp->index * 2 + 1))) {
1691 					/*
1692 					 * A group with invalid rings was
1693 					 * attempted to bind to this share
1694 					 */
1695 					return (EINVAL);
1696 				}
1697 			}
1698 		}
1699 
1700 		vr->tdc_tbl = nxge->pt_config.hw_config.def_mac_txdma_grpid +
1701 		    rg->gindex;
1702 		rg->sindex = vr->region;
1703 		break;
1704 	}
1705 	return (0);
1706 }
1707 
1708 int
1709 nxge_hio_share_rem_group(mac_share_handle_t shandle,
1710     mac_group_driver_t ghandle)
1711 {
1712 	nxge_share_handle_t	*shp = (nxge_share_handle_t *)shandle;
1713 	nxge_ring_group_t	*group = (nxge_ring_group_t *)ghandle;
1714 	nxge_hio_vr_t		*vr;	/* The Virtualization Region */
1715 	int			rv = 0;
1716 
1717 	vr = shp->vrp;
1718 
1719 	switch (group->type) {
1720 	case MAC_RING_TYPE_RX:
1721 		group->sindex = 0;
1722 		vr->rdc_tbl = 0;
1723 		shp->rxgroup = 0;
1724 		break;
1725 
1726 	case MAC_RING_TYPE_TX:
1727 		group->sindex = 0;
1728 		vr->tdc_tbl = 0;
1729 		break;
1730 	}
1731 
1732 	return (rv);
1733 }
1734 
1735 int
1736 nxge_hio_share_bind(mac_share_handle_t shandle, uint64_t cookie,
1737     uint64_t *rcookie)
1738 {
1739 	nxge_t			*nxge;
1740 	nxge_share_handle_t	*shp = (nxge_share_handle_t *)shandle;
1741 	nxge_hio_vr_t		*vr;
1742 	uint64_t		rmap, tmap, hv_rmap, hv_tmap;
1743 	int			rv;
1744 
1745 	nxge = shp->nxgep;
1746 	vr = (nxge_hio_vr_t *)shp->vrp;
1747 
1748 	/*
1749 	 * Add resources to the share.
1750 	 * For each DMA channel associated with the VR, bind its resources
1751 	 * to the VR.
1752 	 */
1753 	tmap = 0;
1754 	rv = nxge_hio_addres(vr, MAC_RING_TYPE_TX, &tmap);
1755 	if (rv != 0) {
1756 		return (rv);
1757 	}
1758 
1759 	rmap = 0;
1760 	rv = nxge_hio_addres(vr, MAC_RING_TYPE_RX, &rmap);
1761 	if (rv != 0) {
1762 		nxge_hio_remres(vr, MAC_RING_TYPE_TX, tmap);
1763 		return (rv);
1764 	}
1765 
1766 	/*
1767 	 * Ask the Hypervisor to set up the VR and allocate slots for
1768 	 * each rings associated with the VR.
1769 	 */
1770 	hv_tmap = hv_rmap = 0;
1771 	if ((rv = nxge_hio_share_assign(nxge, cookie,
1772 	    &hv_tmap, &hv_rmap, vr))) {
1773 		nxge_hio_remres(vr, MAC_RING_TYPE_TX, tmap);
1774 		nxge_hio_remres(vr, MAC_RING_TYPE_RX, rmap);
1775 		return (rv);
1776 	}
1777 
1778 	shp->active = B_TRUE;
1779 	shp->tmap = hv_tmap;
1780 	shp->rmap = hv_rmap;
1781 
1782 	/* high 32 bits are cfg_hdl and low 32 bits are HV cookie */
1783 	*rcookie = (((uint64_t)nxge->niu_cfg_hdl) << 32) | vr->cookie;
1784 
1785 	return (0);
1786 }
1787 
1788 void
1789 nxge_hio_share_unbind(mac_share_handle_t shandle)
1790 {
1791 	nxge_share_handle_t *shp = (nxge_share_handle_t *)shandle;
1792 
1793 	/*
1794 	 * First, unassign the VR (take it back),
1795 	 * so we can enable interrupts again.
1796 	 */
1797 	nxge_hio_share_unassign(shp->vrp);
1798 
1799 	/*
1800 	 * Free Ring Resources for TX and RX
1801 	 */
1802 	nxge_hio_remres(shp->vrp, MAC_RING_TYPE_TX, shp->tmap);
1803 	nxge_hio_remres(shp->vrp, MAC_RING_TYPE_RX, shp->rmap);
1804 }
1805 
1806 
1807 /*
1808  * nxge_hio_vr_share
1809  *
1810  *	Find an unused Virtualization Region (VR).
1811  *
1812  * Arguments:
1813  * 	nxge
1814  *
1815  * Notes:
1816  *
1817  * Context:
1818  *	Service domain
1819  */
1820 nxge_hio_vr_t *
1821 nxge_hio_vr_share(
1822 	nxge_t *nxge)
1823 {
1824 	nxge_hio_data_t *nhd = (nxge_hio_data_t *)nxge->nxge_hw_p->hio;
1825 	nxge_hio_vr_t *vr;
1826 
1827 	int first, limit, region;
1828 
1829 	NXGE_DEBUG_MSG((nxge, HIO_CTL, "==> nxge_hio_vr_share"));
1830 
1831 	MUTEX_ENTER(&nhd->lock);
1832 
1833 	if (nhd->vrs == 0) {
1834 		MUTEX_EXIT(&nhd->lock);
1835 		return (0);
1836 	}
1837 
1838 	/* Find an empty virtual region (VR). */
1839 	if (nxge->function_num == 0) {
1840 		// FUNC0_VIR0 'belongs' to NIU port 0.
1841 		first = FUNC0_VIR1;
1842 		limit = FUNC2_VIR0;
1843 	} else if (nxge->function_num == 1) {
1844 		// FUNC2_VIR0 'belongs' to NIU port 1.
1845 		first = FUNC2_VIR1;
1846 		limit = FUNC_VIR_MAX;
1847 	} else {
1848 		cmn_err(CE_WARN,
1849 		    "Shares not supported on function(%d) at this time.\n",
1850 		    nxge->function_num);
1851 	}
1852 
1853 	for (region = first; region < limit; region++) {
1854 		if (nhd->vr[region].nxge == 0)
1855 			break;
1856 	}
1857 
1858 	if (region == limit) {
1859 		MUTEX_EXIT(&nhd->lock);
1860 		return (0);
1861 	}
1862 
1863 	vr = &nhd->vr[region];
1864 	vr->nxge = (uintptr_t)nxge;
1865 	vr->region = (uintptr_t)region;
1866 
1867 	nhd->vrs--;
1868 
1869 	MUTEX_EXIT(&nhd->lock);
1870 
1871 	NXGE_DEBUG_MSG((nxge, HIO_CTL, "<== nxge_hio_vr_share"));
1872 
1873 	return (vr);
1874 }
1875 
1876 void
1877 nxge_hio_unshare(
1878 	nxge_hio_vr_t *vr)
1879 {
1880 	nxge_t *nxge = (nxge_t *)vr->nxge;
1881 	nxge_hio_data_t *nhd;
1882 
1883 	vr_region_t region;
1884 
1885 	NXGE_DEBUG_MSG((nxge, HIO_CTL, "==> nxge_hio_unshare"));
1886 
1887 	if (!nxge) {
1888 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL, "nxge_hio_unshare: "
1889 		    "vr->nxge is NULL"));
1890 		return;
1891 	}
1892 
1893 	/*
1894 	 * This function is no longer called, but I will keep it
1895 	 * here in case we want to revisit this topic in the future.
1896 	 *
1897 	 * nxge_hio_hostinfo_uninit(nxge, vr);
1898 	 */
1899 
1900 	/*
1901 	 * XXX: This is done by ms_sremove?
1902 	 * (void) nxge_fzc_rdc_tbl_unbind(nxge, vr->rdc_tbl);
1903 	 */
1904 
1905 	nhd = (nxge_hio_data_t *)nxge->nxge_hw_p->hio;
1906 
1907 	MUTEX_ENTER(&nhd->lock);
1908 
1909 	region = vr->region;
1910 	(void) memset(vr, 0, sizeof (*vr));
1911 	vr->region = region;
1912 
1913 	nhd->vrs++;
1914 
1915 	MUTEX_EXIT(&nhd->lock);
1916 
1917 	NXGE_DEBUG_MSG((nxge, HIO_CTL, "<== nxge_hio_unshare"));
1918 }
1919 
1920 int
1921 nxge_hio_addres(
1922 	nxge_hio_vr_t *vr,
1923 	mac_ring_type_t type,
1924 	uint64_t *map)
1925 {
1926 	nxge_t		*nxge = (nxge_t *)vr->nxge;
1927 	nxge_grp_t	*group;
1928 	int		groupid;
1929 	int		i;
1930 	int		max_dcs;
1931 
1932 	NXGE_DEBUG_MSG((nxge, HIO_CTL, "==> nxge_hio_addres"));
1933 
1934 	if (!nxge)
1935 		return (EINVAL);
1936 
1937 	/*
1938 	 * For each ring associated with the group, add the resources
1939 	 * to the group and bind.
1940 	 */
1941 	max_dcs = (type == MAC_RING_TYPE_TX) ? NXGE_MAX_TDCS : NXGE_MAX_RDCS;
1942 	if (type == MAC_RING_TYPE_TX) {
1943 		/* set->group is an array of group indexed by a port group id */
1944 		groupid = vr->tdc_tbl -
1945 		    nxge->pt_config.hw_config.def_mac_txdma_grpid;
1946 		group = nxge->tx_set.group[groupid];
1947 	} else {
1948 		/* set->group is an array of group indexed by a port group id */
1949 		groupid = vr->rdc_tbl -
1950 		    nxge->pt_config.hw_config.def_mac_rxdma_grpid;
1951 		group = nxge->rx_set.group[groupid];
1952 	}
1953 
1954 	if (group->map == 0) {
1955 		NXGE_DEBUG_MSG((nxge, HIO_CTL, "There is no rings associated "
1956 		    "with this VR"));
1957 		return (EINVAL);
1958 	}
1959 
1960 	for (i = 0; i < max_dcs; i++) {
1961 		if (group->map & (1 << i)) {
1962 			int rv;
1963 
1964 			if ((rv = nxge_hio_dc_share(nxge, vr, type, i)) < 0) {
1965 				if (*map == 0) /* Couldn't get even one DC. */
1966 					return (-rv);
1967 				else
1968 					break;
1969 			}
1970 			*map |= (1 << i);
1971 		}
1972 	}
1973 
1974 	NXGE_DEBUG_MSG((nxge, HIO_CTL, "<== nxge_hio_addres"));
1975 
1976 	return (0);
1977 }
1978 
1979 /* ARGSUSED */
1980 void
1981 nxge_hio_remres(
1982 	nxge_hio_vr_t *vr,
1983 	mac_ring_type_t type,
1984 	res_map_t res_map)
1985 {
1986 	nxge_t *nxge = (nxge_t *)vr->nxge;
1987 	nxge_grp_t *group;
1988 
1989 	if (!nxge) {
1990 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL, "nxge_hio_remres: "
1991 		    "vr->nxge is NULL"));
1992 		return;
1993 	}
1994 
1995 	NXGE_DEBUG_MSG((nxge, HIO_CTL, "==> nxge_hio_remres(%lx)", res_map));
1996 
1997 	/*
1998 	 * For each ring bound to the group, remove the DMA resources
1999 	 * from the group and unbind.
2000 	 */
2001 	group = (type == MAC_RING_TYPE_TX ? &vr->tx_group : &vr->rx_group);
2002 	while (group->dc) {
2003 		nxge_hio_dc_t *dc = group->dc;
2004 		NXGE_DC_RESET(res_map, dc->page);
2005 		nxge_hio_dc_unshare(nxge, vr, type, dc->channel);
2006 	}
2007 
2008 	if (res_map) {
2009 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL, "nxge_hio_remres: "
2010 		    "res_map %lx", res_map));
2011 	}
2012 
2013 	NXGE_DEBUG_MSG((nxge, HIO_CTL, "<== nxge_hio_remres"));
2014 }
2015 
2016 /*
2017  * nxge_hio_tdc_share
2018  *
2019  *	Share an unused TDC channel.
2020  *
2021  * Arguments:
2022  * 	nxge
2023  *
2024  * Notes:
2025  *
2026  * A.7.3 Reconfigure Tx DMA channel
2027  *	Disable TxDMA			A.9.6.10
2028  *     [Rebind TxDMA channel to Port	A.9.6.7]
2029  *
2030  * We don't have to Rebind the TDC to the port - it always already bound.
2031  *
2032  *	Soft Reset TxDMA		A.9.6.2
2033  *
2034  * This procedure will be executed by nxge_init_txdma_channel() in the
2035  * guest domain:
2036  *
2037  *	Re-initialize TxDMA		A.9.6.8
2038  *	Reconfigure TxDMA
2039  *	Enable TxDMA			A.9.6.9
2040  *
2041  * Context:
2042  *	Service domain
2043  */
2044 int
2045 nxge_hio_tdc_share(
2046 	nxge_t *nxge,
2047 	int channel)
2048 {
2049 	nxge_hio_data_t *nhd = (nxge_hio_data_t *)nxge->nxge_hw_p->hio;
2050 	nxge_grp_set_t *set = &nxge->tx_set;
2051 	tx_ring_t *ring;
2052 	int count;
2053 
2054 	NXGE_DEBUG_MSG((nxge, HIO_CTL, "==> nxge_hio_tdc_share"));
2055 
2056 	/*
2057 	 * Wait until this channel is idle.
2058 	 */
2059 	ring = nxge->tx_rings->rings[channel];
2060 	ASSERT(ring != NULL);
2061 
2062 	(void) atomic_swap_32(&ring->tx_ring_offline, NXGE_TX_RING_OFFLINING);
2063 	if (ring->tx_ring_busy) {
2064 		/*
2065 		 * Wait for 30 seconds.
2066 		 */
2067 		for (count = 30 * 1000; count; count--) {
2068 			if (ring->tx_ring_offline & NXGE_TX_RING_OFFLINED) {
2069 				break;
2070 			}
2071 
2072 			drv_usecwait(1000);
2073 		}
2074 
2075 		if (count == 0) {
2076 			(void) atomic_swap_32(&ring->tx_ring_offline,
2077 			    NXGE_TX_RING_ONLINE);
2078 			NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
2079 			    "nxge_hio_tdc_share: "
2080 			    "Tx ring %d was always BUSY", channel));
2081 			return (-EIO);
2082 		}
2083 	} else {
2084 		(void) atomic_swap_32(&ring->tx_ring_offline,
2085 		    NXGE_TX_RING_OFFLINED);
2086 	}
2087 
2088 	MUTEX_ENTER(&nhd->lock);
2089 	nxge->tdc_is_shared[channel] = B_TRUE;
2090 	MUTEX_EXIT(&nhd->lock);
2091 
2092 	if (nxge_intr_remove(nxge, VP_BOUND_TX, channel) != NXGE_OK) {
2093 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL, "nxge_hio_tdc_share: "
2094 		    "Failed to remove interrupt for TxDMA channel %d",
2095 		    channel));
2096 		return (-EINVAL);
2097 	}
2098 
2099 	/* Disable TxDMA A.9.6.10 */
2100 	(void) nxge_txdma_channel_disable(nxge, channel);
2101 
2102 	/* The SD is sharing this channel. */
2103 	NXGE_DC_SET(set->shared.map, channel);
2104 	set->shared.count++;
2105 
2106 	/* Soft Reset TxDMA A.9.6.2 */
2107 	nxge_grp_dc_remove(nxge, VP_BOUND_TX, channel);
2108 
2109 	/*
2110 	 * Initialize the DC-specific FZC control registers.
2111 	 * -----------------------------------------------------
2112 	 */
2113 	if (nxge_init_fzc_tdc(nxge, channel) != NXGE_OK) {
2114 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
2115 		    "nxge_hio_tdc_share: FZC TDC failed: %d", channel));
2116 		return (-EIO);
2117 	}
2118 
2119 	NXGE_DEBUG_MSG((nxge, HIO_CTL, "<== nxge_hio_tdc_share"));
2120 
2121 	return (0);
2122 }
2123 
2124 /*
2125  * nxge_hio_rdc_share
2126  *
2127  *	Share an unused RDC channel.
2128  *
2129  * Arguments:
2130  * 	nxge
2131  *
2132  * Notes:
2133  *
2134  * This is the latest version of the procedure to
2135  * Reconfigure an Rx DMA channel:
2136  *
2137  * A.6.3 Reconfigure Rx DMA channel
2138  *	Stop RxMAC		A.9.2.6
2139  *	Drain IPP Port		A.9.3.6
2140  *	Stop and reset RxDMA	A.9.5.3
2141  *
2142  * This procedure will be executed by nxge_init_rxdma_channel() in the
2143  * guest domain:
2144  *
2145  *	Initialize RxDMA	A.9.5.4
2146  *	Reconfigure RxDMA
2147  *	Enable RxDMA		A.9.5.5
2148  *
2149  * We will do this here, since the RDC is a canalis non grata:
2150  *	Enable RxMAC		A.9.2.10
2151  *
2152  * Context:
2153  *	Service domain
2154  */
2155 int
2156 nxge_hio_rdc_share(
2157 	nxge_t *nxge,
2158 	nxge_hio_vr_t *vr,
2159 	int channel)
2160 {
2161 	nxge_grp_set_t *set = &nxge->rx_set;
2162 	nxge_rdc_grp_t *rdc_grp;
2163 
2164 	NXGE_DEBUG_MSG((nxge, HIO_CTL, "==> nxge_hio_rdc_share"));
2165 
2166 	/* Disable interrupts. */
2167 	if (nxge_intr_remove(nxge, VP_BOUND_RX, channel) != NXGE_OK) {
2168 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL, "nxge_hio_rdc_share: "
2169 		    "Failed to remove interrupt for RxDMA channel %d",
2170 		    channel));
2171 		return (NXGE_ERROR);
2172 	}
2173 
2174 	/* Stop RxMAC = A.9.2.6 */
2175 	if (nxge_rx_mac_disable(nxge) != NXGE_OK) {
2176 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL, "nxge_hio_rdc_share: "
2177 		    "Failed to disable RxMAC"));
2178 	}
2179 
2180 	/* Drain IPP Port = A.9.3.6 */
2181 	(void) nxge_ipp_drain(nxge);
2182 
2183 	/* Stop and reset RxDMA = A.9.5.3 */
2184 	// De-assert EN: RXDMA_CFIG1[31] = 0 (DMC+00000 )
2185 	if (nxge_disable_rxdma_channel(nxge, channel) != NXGE_OK) {
2186 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL, "nxge_hio_rdc_share: "
2187 		    "Failed to disable RxDMA channel %d", channel));
2188 	}
2189 
2190 	/* The SD is sharing this channel. */
2191 	NXGE_DC_SET(set->shared.map, channel);
2192 	set->shared.count++;
2193 
2194 	// Assert RST: RXDMA_CFIG1[30] = 1
2195 	nxge_grp_dc_remove(nxge, VP_BOUND_RX, channel);
2196 
2197 	/*
2198 	 * The guest domain will reconfigure the RDC later.
2199 	 *
2200 	 * But in the meantime, we must re-enable the Rx MAC so
2201 	 * that we can start receiving packets again on the
2202 	 * remaining RDCs:
2203 	 *
2204 	 * Enable RxMAC = A.9.2.10
2205 	 */
2206 	if (nxge_rx_mac_enable(nxge) != NXGE_OK) {
2207 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
2208 		    "nxge_hio_rdc_share: Rx MAC still disabled"));
2209 	}
2210 
2211 	/*
2212 	 * Initialize the DC-specific FZC control registers.
2213 	 * -----------------------------------------------------
2214 	 */
2215 	if (nxge_init_fzc_rdc(nxge, channel) != NXGE_OK) {
2216 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
2217 		    "nxge_hio_rdc_share: RZC RDC failed: %ld", channel));
2218 		return (-EIO);
2219 	}
2220 
2221 	/*
2222 	 * Update the RDC group.
2223 	 */
2224 	rdc_grp = &nxge->pt_config.rdc_grps[vr->rdc_tbl];
2225 	NXGE_DC_SET(rdc_grp->map, channel);
2226 
2227 	NXGE_DEBUG_MSG((nxge, HIO_CTL, "<== nxge_hio_rdc_share"));
2228 
2229 	return (0);
2230 }
2231 
2232 /*
2233  * nxge_hio_dc_share
2234  *
2235  *	Share a DMA channel with a guest domain.
2236  *
2237  * Arguments:
2238  * 	nxge
2239  * 	vr	The VR that <channel> will belong to.
2240  * 	type	Tx or Rx.
2241  * 	channel	Channel to share
2242  *
2243  * Notes:
2244  *
2245  * Context:
2246  *	Service domain
2247  */
2248 int
2249 nxge_hio_dc_share(
2250 	nxge_t *nxge,
2251 	nxge_hio_vr_t *vr,
2252 	mac_ring_type_t type,
2253 	int channel)
2254 {
2255 	nxge_hio_data_t *nhd = (nxge_hio_data_t *)nxge->nxge_hw_p->hio;
2256 	nxge_hio_dc_t *dc;
2257 	nxge_grp_t *group;
2258 	int slot;
2259 
2260 	NXGE_DEBUG_MSG((nxge, HIO_CTL, "==> nxge_hio_dc_share(%cdc %d",
2261 	    type == MAC_RING_TYPE_TX ? 't' : 'r', channel));
2262 
2263 
2264 	/* -------------------------------------------------- */
2265 	slot = (type == MAC_RING_TYPE_TX) ?
2266 	    nxge_hio_tdc_share(nxge, channel) :
2267 	    nxge_hio_rdc_share(nxge, vr, channel);
2268 
2269 	if (slot < 0) {
2270 		if (type == MAC_RING_TYPE_RX) {
2271 			nxge_hio_rdc_unshare(nxge, vr->rdc_tbl, channel);
2272 		} else {
2273 			nxge_hio_tdc_unshare(nxge, vr->tdc_tbl, channel);
2274 		}
2275 		return (slot);
2276 	}
2277 
2278 	MUTEX_ENTER(&nhd->lock);
2279 
2280 	/*
2281 	 * Tag this channel.
2282 	 * --------------------------------------------------
2283 	 */
2284 	dc = type == MAC_RING_TYPE_TX ? &nhd->tdc[channel] : &nhd->rdc[channel];
2285 
2286 	dc->vr = vr;
2287 	dc->channel = (nxge_channel_t)channel;
2288 
2289 	MUTEX_EXIT(&nhd->lock);
2290 
2291 	/*
2292 	 * vr->[t|r]x_group is used by the service domain to
2293 	 * keep track of its shared DMA channels.
2294 	 */
2295 	MUTEX_ENTER(&nxge->group_lock);
2296 	group = (type == MAC_RING_TYPE_TX ? &vr->tx_group : &vr->rx_group);
2297 
2298 	dc->group = group;
2299 	/* Initialize <group>, if necessary */
2300 	if (group->count == 0) {
2301 		group->nxge = nxge;
2302 		group->type = (type == MAC_RING_TYPE_TX) ?
2303 		    VP_BOUND_TX : VP_BOUND_RX;
2304 		group->sequence	= nhd->sequence++;
2305 		group->active = B_TRUE;
2306 	}
2307 
2308 	MUTEX_EXIT(&nxge->group_lock);
2309 
2310 	NXGE_ERROR_MSG((nxge, HIO_CTL,
2311 	    "DC share: %cDC %d was assigned to slot %d",
2312 	    type == MAC_RING_TYPE_TX ? 'T' : 'R', channel, slot));
2313 
2314 	nxge_grp_dc_append(nxge, group, dc);
2315 
2316 	NXGE_DEBUG_MSG((nxge, HIO_CTL, "<== nxge_hio_dc_share"));
2317 
2318 	return (0);
2319 }
2320 
2321 /*
2322  * nxge_hio_tdc_unshare
2323  *
2324  *	Unshare a TDC.
2325  *
2326  * Arguments:
2327  * 	nxge
2328  * 	channel	The channel to unshare (add again).
2329  *
2330  * Notes:
2331  *
2332  * Context:
2333  *	Service domain
2334  */
2335 void
2336 nxge_hio_tdc_unshare(
2337 	nxge_t *nxge,
2338 	int dev_grpid,
2339 	int channel)
2340 {
2341 	nxge_grp_set_t *set = &nxge->tx_set;
2342 	nxge_grp_t *group;
2343 	int grpid;
2344 
2345 	NXGE_DEBUG_MSG((nxge, HIO_CTL, "==> nxge_hio_tdc_unshare"));
2346 
2347 	NXGE_DC_RESET(set->shared.map, channel);
2348 	set->shared.count--;
2349 
2350 	grpid = dev_grpid - nxge->pt_config.hw_config.def_mac_txdma_grpid;
2351 	group = set->group[grpid];
2352 
2353 	if ((nxge_grp_dc_add(nxge, group, VP_BOUND_TX, channel))) {
2354 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL, "nxge_hio_tdc_unshare: "
2355 		    "Failed to initialize TxDMA channel %d", channel));
2356 		return;
2357 	}
2358 
2359 	/* Re-add this interrupt. */
2360 	if (nxge_intr_add(nxge, VP_BOUND_TX, channel) != NXGE_OK) {
2361 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL, "nxge_hio_tdc_unshare: "
2362 		    "Failed to add interrupt for TxDMA channel %d", channel));
2363 	}
2364 
2365 	NXGE_DEBUG_MSG((nxge, HIO_CTL, "<== nxge_hio_tdc_unshare"));
2366 }
2367 
2368 /*
2369  * nxge_hio_rdc_unshare
2370  *
2371  *	Unshare an RDC: add it to the SD's RDC groups (tables).
2372  *
2373  * Arguments:
2374  * 	nxge
2375  * 	channel	The channel to unshare (add again).
2376  *
2377  * Notes:
2378  *
2379  * Context:
2380  *	Service domain
2381  */
2382 void
2383 nxge_hio_rdc_unshare(
2384 	nxge_t *nxge,
2385 	int dev_grpid,
2386 	int channel)
2387 {
2388 	nxge_grp_set_t		*set = &nxge->rx_set;
2389 	nxge_grp_t		*group;
2390 	int			grpid;
2391 
2392 	NXGE_DEBUG_MSG((nxge, HIO_CTL, "==> nxge_hio_rdc_unshare"));
2393 
2394 	/* Stop RxMAC = A.9.2.6 */
2395 	if (nxge_rx_mac_disable(nxge) != NXGE_OK) {
2396 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL, "nxge_hio_rdc_unshare: "
2397 		    "Failed to disable RxMAC"));
2398 	}
2399 
2400 	/* Drain IPP Port = A.9.3.6 */
2401 	(void) nxge_ipp_drain(nxge);
2402 
2403 	/* Stop and reset RxDMA = A.9.5.3 */
2404 	// De-assert EN: RXDMA_CFIG1[31] = 0 (DMC+00000 )
2405 	if (nxge_disable_rxdma_channel(nxge, channel) != NXGE_OK) {
2406 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL, "nxge_hio_rdc_unshare: "
2407 		    "Failed to disable RxDMA channel %d", channel));
2408 	}
2409 
2410 	NXGE_DC_RESET(set->shared.map, channel);
2411 	set->shared.count--;
2412 
2413 	grpid = dev_grpid - nxge->pt_config.hw_config.def_mac_rxdma_grpid;
2414 	group = set->group[grpid];
2415 
2416 	/*
2417 	 * Assert RST: RXDMA_CFIG1[30] = 1
2418 	 *
2419 	 * Initialize RxDMA	A.9.5.4
2420 	 * Reconfigure RxDMA
2421 	 * Enable RxDMA		A.9.5.5
2422 	 */
2423 	if ((nxge_grp_dc_add(nxge, group, VP_BOUND_RX, channel))) {
2424 		/* Be sure to re-enable the RX MAC. */
2425 		if (nxge_rx_mac_enable(nxge) != NXGE_OK) {
2426 			NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
2427 			    "nxge_hio_rdc_share: Rx MAC still disabled"));
2428 		}
2429 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL, "nxge_hio_rdc_unshare: "
2430 		    "Failed to initialize RxDMA channel %d", channel));
2431 		return;
2432 	}
2433 
2434 	/*
2435 	 * Enable RxMAC = A.9.2.10
2436 	 */
2437 	if (nxge_rx_mac_enable(nxge) != NXGE_OK) {
2438 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
2439 		    "nxge_hio_rdc_share: Rx MAC still disabled"));
2440 		return;
2441 	}
2442 
2443 	/* Re-add this interrupt. */
2444 	if (nxge_intr_add(nxge, VP_BOUND_RX, channel) != NXGE_OK) {
2445 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
2446 		    "nxge_hio_rdc_unshare: Failed to add interrupt for "
2447 		    "RxDMA CHANNEL %d", channel));
2448 	}
2449 
2450 	NXGE_DEBUG_MSG((nxge, HIO_CTL, "<== nxge_hio_rdc_unshare"));
2451 }
2452 
2453 /*
2454  * nxge_hio_dc_unshare
2455  *
2456  *	Unshare (reuse) a DMA channel.
2457  *
2458  * Arguments:
2459  * 	nxge
2460  * 	vr	The VR that <channel> belongs to.
2461  * 	type	Tx or Rx.
2462  * 	channel	The DMA channel to reuse.
2463  *
2464  * Notes:
2465  *
2466  * Context:
2467  *	Service domain
2468  */
2469 void
2470 nxge_hio_dc_unshare(
2471 	nxge_t *nxge,
2472 	nxge_hio_vr_t *vr,
2473 	mac_ring_type_t type,
2474 	int channel)
2475 {
2476 	nxge_grp_t *group;
2477 	nxge_hio_dc_t *dc;
2478 
2479 	NXGE_DEBUG_MSG((nxge, HIO_CTL, "==> nxge_hio_dc_unshare(%cdc %d)",
2480 	    type == MAC_RING_TYPE_TX ? 't' : 'r', channel));
2481 
2482 	/* Unlink the channel from its group. */
2483 	/* -------------------------------------------------- */
2484 	group = (type == MAC_RING_TYPE_TX) ? &vr->tx_group : &vr->rx_group;
2485 	NXGE_DC_RESET(group->map, channel);
2486 	if ((dc = nxge_grp_dc_unlink(nxge, group, channel)) == 0) {
2487 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
2488 		    "nxge_hio_dc_unshare(%d) failed", channel));
2489 		return;
2490 	}
2491 
2492 	dc->vr = 0;
2493 	dc->cookie = 0;
2494 
2495 	if (type == MAC_RING_TYPE_RX) {
2496 		nxge_hio_rdc_unshare(nxge, vr->rdc_tbl, channel);
2497 	} else {
2498 		nxge_hio_tdc_unshare(nxge, vr->tdc_tbl, channel);
2499 	}
2500 
2501 	NXGE_DEBUG_MSG((nxge, HIO_CTL, "<== nxge_hio_dc_unshare"));
2502 }
2503 
2504 
2505 /*
2506  * nxge_hio_rxdma_bind_intr():
2507  *
2508  *	For the guest domain driver, need to bind the interrupt group
2509  *	and state to the rx_rcr_ring_t.
2510  */
2511 
2512 int
2513 nxge_hio_rxdma_bind_intr(nxge_t *nxge, rx_rcr_ring_t *ring, int channel)
2514 {
2515 	nxge_hio_dc_t	*dc;
2516 	nxge_ldgv_t	*control;
2517 	nxge_ldg_t	*group;
2518 	nxge_ldv_t	*device;
2519 
2520 	/*
2521 	 * Find the DMA channel.
2522 	 */
2523 	if (!(dc = nxge_grp_dc_find(nxge, VP_BOUND_RX, channel))) {
2524 		return (NXGE_ERROR);
2525 	}
2526 
2527 	/*
2528 	 * Get the control structure.
2529 	 */
2530 	control = nxge->ldgvp;
2531 	if (control == NULL) {
2532 		return (NXGE_ERROR);
2533 	}
2534 
2535 	group = &control->ldgp[dc->ldg.vector];
2536 	device = &control->ldvp[dc->ldg.ldsv];
2537 
2538 	MUTEX_ENTER(&ring->lock);
2539 	ring->ldgp = group;
2540 	ring->ldvp = device;
2541 	MUTEX_EXIT(&ring->lock);
2542 
2543 	return (NXGE_OK);
2544 }
2545 #endif	/* if defined(sun4v) */
2546