xref: /titanic_51/usr/src/uts/common/io/nxge/nxge_hio.c (revision 3744b6bbfe79c25ef99cb26f7eea17f12271d221)
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 	int			rdctbl;
1252 	int			dev_gindex;
1253 
1254 	ASSERT(group->type == MAC_RING_TYPE_RX);
1255 
1256 #ifdef later
1257 	ASSERT(group->nxgep->nxge_mac_state == NXGE_MAC_STARTED);
1258 #endif
1259 	if (group->nxgep->nxge_mac_state != NXGE_MAC_STARTED)
1260 		return (ENXIO);
1261 
1262 	mutex_enter(group->nxgep->genlock);
1263 	dev_gindex = group->nxgep->pt_config.hw_config.def_mac_rxdma_grpid +
1264 	    group->gindex;
1265 
1266 	/*
1267 	 * Get an rdc table for this group.
1268 	 * Group ID is given by the caller, and that's the group it needs
1269 	 * to bind to.  The default group is already bound when the driver
1270 	 * was attached.
1271 	 *
1272 	 * For Group 0, it's RDC table was allocated at attach time
1273 	 * no need to allocate a new table.
1274 	 */
1275 	if (group->gindex != 0) {
1276 		rdctbl = nxge_fzc_rdc_tbl_bind(group->nxgep,
1277 		    dev_gindex, B_TRUE);
1278 		if (rdctbl < 0) {
1279 			mutex_exit(group->nxgep->genlock);
1280 			return (rdctbl);
1281 		}
1282 	} else {
1283 		rdctbl = group->nxgep->pt_config.hw_config.def_mac_rxdma_grpid;
1284 	}
1285 
1286 	group->rdctbl = rdctbl;
1287 
1288 	(void) nxge_init_fzc_rdc_tbl(group->nxgep, rdctbl);
1289 
1290 	group->started = B_TRUE;
1291 	mutex_exit(group->nxgep->genlock);
1292 
1293 	return (0);
1294 }
1295 
1296 static void
1297 nxge_hio_group_stop(mac_group_driver_t gdriver)
1298 {
1299 	nxge_ring_group_t *group = (nxge_ring_group_t *)gdriver;
1300 
1301 	ASSERT(group->type == MAC_RING_TYPE_RX);
1302 
1303 	mutex_enter(group->nxgep->genlock);
1304 	group->started = B_FALSE;
1305 
1306 	/*
1307 	 * Unbind the RDC table previously bound for this group.
1308 	 *
1309 	 * Since RDC table for group 0 was allocated at attach
1310 	 * time, no need to unbind the table here.
1311 	 */
1312 	if (group->gindex != 0)
1313 		(void) nxge_fzc_rdc_tbl_unbind(group->nxgep, group->rdctbl);
1314 
1315 	mutex_exit(group->nxgep->genlock);
1316 }
1317 
1318 /* ARGSUSED */
1319 void
1320 nxge_hio_group_get(void *arg, mac_ring_type_t type, int groupid,
1321 	mac_group_info_t *infop, mac_group_handle_t ghdl)
1322 {
1323 	p_nxge_t		nxgep = (p_nxge_t)arg;
1324 	nxge_ring_group_t	*group;
1325 	int			dev_gindex;
1326 
1327 	switch (type) {
1328 	case MAC_RING_TYPE_RX:
1329 		group = &nxgep->rx_hio_groups[groupid];
1330 		group->nxgep = nxgep;
1331 		group->ghandle = ghdl;
1332 		group->gindex = groupid;
1333 		group->sindex = 0;	/* not yet bound to a share */
1334 
1335 		dev_gindex = nxgep->pt_config.hw_config.def_mac_rxdma_grpid +
1336 		    groupid;
1337 
1338 		if (nxgep->pt_config.hw_config.def_mac_rxdma_grpid ==
1339 		    dev_gindex)
1340 			group->port_default_grp = B_TRUE;
1341 
1342 		infop->mgi_driver = (mac_group_driver_t)group;
1343 		infop->mgi_start = nxge_hio_group_start;
1344 		infop->mgi_stop = nxge_hio_group_stop;
1345 		infop->mgi_addmac = nxge_hio_add_mac;
1346 		infop->mgi_remmac = nxge_hio_rem_mac;
1347 		infop->mgi_count =
1348 		    nxgep->pt_config.rdc_grps[dev_gindex].max_rdcs;
1349 		break;
1350 
1351 	case MAC_RING_TYPE_TX:
1352 		/*
1353 		 * 'groupid' for TX should be incremented by one since
1354 		 * the default group (groupid 0) is not known by the MAC layer
1355 		 */
1356 		group = &nxgep->tx_hio_groups[groupid + 1];
1357 		group->nxgep = nxgep;
1358 		group->ghandle = ghdl;
1359 		group->gindex = groupid + 1;
1360 		group->sindex = 0;	/* not yet bound to a share */
1361 
1362 		infop->mgi_driver = (mac_group_driver_t)group;
1363 		infop->mgi_start = NULL;
1364 		infop->mgi_stop = NULL;
1365 		infop->mgi_addmac = NULL;	/* not needed */
1366 		infop->mgi_remmac = NULL;	/* not needed */
1367 		/* no rings associated with group initially */
1368 		infop->mgi_count = 0;
1369 		break;
1370 	}
1371 }
1372 
1373 #if defined(sun4v)
1374 
1375 int
1376 nxge_hio_share_assign(
1377 	nxge_t *nxge,
1378 	uint64_t cookie,
1379 	res_map_t *tmap,
1380 	res_map_t *rmap,
1381 	nxge_hio_vr_t *vr)
1382 {
1383 	nxge_hio_data_t *nhd = (nxge_hio_data_t *)nxge->nxge_hw_p->hio;
1384 	uint64_t slot, hv_rv;
1385 	nxge_hio_dc_t *dc;
1386 	nxhv_vr_fp_t *fp;
1387 	int i;
1388 
1389 	/*
1390 	 * Ask the Hypervisor to set up the VR for us
1391 	 */
1392 	fp = &nhd->hio.vr;
1393 	if ((hv_rv = (*fp->assign)(vr->region, cookie, &vr->cookie))) {
1394 		NXGE_ERROR_MSG((nxge, HIO_CTL,
1395 		    "nxge_hio_share_assign: "
1396 		    "vr->assign() returned %d", hv_rv));
1397 		return (-EIO);
1398 	}
1399 
1400 	/*
1401 	 * For each shared TDC, ask the HV to find us an empty slot.
1402 	 * -----------------------------------------------------
1403 	 */
1404 	dc = vr->tx_group.dc;
1405 	for (i = 0; i < NXGE_MAX_TDCS; i++) {
1406 		nxhv_dc_fp_t *tx = &nhd->hio.tx;
1407 		while (dc) {
1408 			hv_rv = (*tx->assign)
1409 			    (vr->cookie, dc->channel, &slot);
1410 			if (hv_rv != 0) {
1411 				NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
1412 				    "nxge_hio_share_assign: "
1413 				    "tx->assign(%x, %d) failed: %ld",
1414 				    vr->cookie, dc->channel, hv_rv));
1415 				return (-EIO);
1416 			}
1417 
1418 			dc->cookie = vr->cookie;
1419 			dc->page = (vp_channel_t)slot;
1420 
1421 			/* Inform the caller about the slot chosen. */
1422 			(*tmap) |= 1 << slot;
1423 
1424 			dc = dc->next;
1425 		}
1426 	}
1427 
1428 	/*
1429 	 * For each shared RDC, ask the HV to find us an empty slot.
1430 	 * -----------------------------------------------------
1431 	 */
1432 	dc = vr->rx_group.dc;
1433 	for (i = 0; i < NXGE_MAX_RDCS; i++) {
1434 		nxhv_dc_fp_t *rx = &nhd->hio.rx;
1435 		while (dc) {
1436 			hv_rv = (*rx->assign)
1437 			    (vr->cookie, dc->channel, &slot);
1438 			if (hv_rv != 0) {
1439 				NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
1440 				    "nxge_hio_share_assign: "
1441 				    "rx->assign(%x, %d) failed: %ld",
1442 				    vr->cookie, dc->channel, hv_rv));
1443 				return (-EIO);
1444 			}
1445 
1446 			dc->cookie = vr->cookie;
1447 			dc->page = (vp_channel_t)slot;
1448 
1449 			/* Inform the caller about the slot chosen. */
1450 			(*rmap) |= 1 << slot;
1451 
1452 			dc = dc->next;
1453 		}
1454 	}
1455 
1456 	return (0);
1457 }
1458 
1459 void
1460 nxge_hio_share_unassign(
1461 	nxge_hio_vr_t *vr)
1462 {
1463 	nxge_t *nxge = (nxge_t *)vr->nxge;
1464 	nxge_hio_data_t *nhd;
1465 	nxge_hio_dc_t *dc;
1466 	nxhv_vr_fp_t *fp;
1467 	uint64_t hv_rv;
1468 
1469 	nhd = (nxge_hio_data_t *)nxge->nxge_hw_p->hio;
1470 
1471 	dc = vr->tx_group.dc;
1472 	while (dc) {
1473 		nxhv_dc_fp_t *tx = &nhd->hio.tx;
1474 		hv_rv = (*tx->unassign)(vr->cookie, dc->page);
1475 		if (hv_rv != 0) {
1476 			NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
1477 			    "nxge_hio_share_unassign: "
1478 			    "tx->unassign(%x, %d) failed: %ld",
1479 			    vr->cookie, dc->page, hv_rv));
1480 		}
1481 		dc = dc->next;
1482 	}
1483 
1484 	dc = vr->rx_group.dc;
1485 	while (dc) {
1486 		nxhv_dc_fp_t *rx = &nhd->hio.rx;
1487 		hv_rv = (*rx->unassign)(vr->cookie, dc->page);
1488 		if (hv_rv != 0) {
1489 			NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
1490 			    "nxge_hio_share_unassign: "
1491 			    "rx->unassign(%x, %d) failed: %ld",
1492 			    vr->cookie, dc->page, hv_rv));
1493 		}
1494 		dc = dc->next;
1495 	}
1496 
1497 	fp = &nhd->hio.vr;
1498 	if (fp->unassign) {
1499 		hv_rv = (*fp->unassign)(vr->cookie);
1500 		if (hv_rv != 0) {
1501 			NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
1502 			    "nxge_hio_share_unassign: "
1503 			    "vr->assign(%x) failed: %ld",
1504 			    vr->cookie, hv_rv));
1505 		}
1506 	}
1507 }
1508 
1509 int
1510 nxge_hio_share_alloc(void *arg, mac_share_handle_t *shandle)
1511 {
1512 	p_nxge_t		nxge = (p_nxge_t)arg;
1513 	nxge_share_handle_t	*shp;
1514 	nxge_hio_vr_t		*vr;	/* The Virtualization Region */
1515 	nxge_hio_data_t		*nhd = (nxge_hio_data_t *)nxge->nxge_hw_p->hio;
1516 
1517 	NXGE_DEBUG_MSG((nxge, HIO_CTL, "==> nxge_hio_share"));
1518 
1519 	if (nhd->hio.vr.assign == 0 || nhd->hio.tx.assign == 0 ||
1520 	    nhd->hio.rx.assign == 0) {
1521 		NXGE_ERROR_MSG((nxge, HIO_CTL, "HV assign function(s) NULL"));
1522 		return (EIO);
1523 	}
1524 
1525 	/*
1526 	 * Get a VR.
1527 	 */
1528 	if ((vr = nxge_hio_vr_share(nxge)) == 0)
1529 		return (EAGAIN);
1530 
1531 	shp = &nxge->shares[vr->region];
1532 	shp->nxgep = nxge;
1533 	shp->index = vr->region;
1534 	shp->vrp = (void *)vr;
1535 	shp->tmap = shp->rmap = 0;	/* to be assigned by ms_sbind */
1536 	shp->rxgroup = 0;		/* to be assigned by ms_sadd */
1537 	shp->active = B_FALSE;		/* not bound yet */
1538 
1539 	*shandle = (mac_share_handle_t)shp;
1540 
1541 	NXGE_DEBUG_MSG((nxge, HIO_CTL, "<== nxge_hio_share"));
1542 	return (0);
1543 }
1544 
1545 
1546 void
1547 nxge_hio_share_free(mac_share_handle_t shandle)
1548 {
1549 	nxge_share_handle_t	*shp = (nxge_share_handle_t *)shandle;
1550 	nxge_hio_vr_t		*vr;
1551 
1552 	/*
1553 	 * Clear internal handle state.
1554 	 */
1555 	vr = shp->vrp;
1556 	shp->vrp = (void *)NULL;
1557 	shp->index = 0;
1558 	shp->tmap = 0;
1559 	shp->rmap = 0;
1560 	shp->rxgroup = 0;
1561 	shp->active = B_FALSE;
1562 
1563 	/*
1564 	 * Free VR resource.
1565 	 */
1566 	nxge_hio_unshare(vr);
1567 }
1568 
1569 
1570 void
1571 nxge_hio_share_query(mac_share_handle_t shandle, mac_ring_type_t type,
1572     mac_ring_handle_t *rings, uint_t *n_rings)
1573 {
1574 	nxge_t			*nxge;
1575 	nxge_share_handle_t	*shp = (nxge_share_handle_t *)shandle;
1576 	nxge_ring_handle_t	*rh;
1577 	uint32_t		offset;
1578 
1579 	nxge = shp->nxgep;
1580 
1581 	switch (type) {
1582 	case MAC_RING_TYPE_RX:
1583 		rh = nxge->rx_ring_handles;
1584 		offset = nxge->pt_config.hw_config.start_rdc;
1585 		break;
1586 
1587 	case MAC_RING_TYPE_TX:
1588 		rh = nxge->tx_ring_handles;
1589 		offset = nxge->pt_config.hw_config.tdc.start;
1590 		break;
1591 	}
1592 
1593 	/*
1594 	 * In version 1.0, we may only give a VR 2 RDCs/TDCs.  Not only that,
1595 	 * but the HV has statically assigned the channels like so:
1596 	 * VR0: RDC0 & RDC1
1597 	 * VR1: RDC2 & RDC3, etc.
1598 	 * The TDCs are assigned in exactly the same way.
1599 	 */
1600 	if (rings != NULL) {
1601 		rings[0] = rh[(shp->index * 2) - offset].ring_handle;
1602 		rings[1] = rh[(shp->index * 2 + 1) - offset].ring_handle;
1603 	}
1604 	if (n_rings != NULL) {
1605 		*n_rings = 2;
1606 	}
1607 }
1608 
1609 int
1610 nxge_hio_share_add_group(mac_share_handle_t shandle,
1611     mac_group_driver_t ghandle)
1612 {
1613 	nxge_t			*nxge;
1614 	nxge_share_handle_t	*shp = (nxge_share_handle_t *)shandle;
1615 	nxge_ring_group_t	*rg = (nxge_ring_group_t *)ghandle;
1616 	nxge_hio_vr_t		*vr;	/* The Virtualization Region */
1617 	nxge_grp_t		*group;
1618 	int			i;
1619 
1620 	if (rg->sindex != 0) {
1621 		/* the group is already bound to a share */
1622 		return (EALREADY);
1623 	}
1624 
1625 	/*
1626 	 * If we are adding a group 0 to a share, this
1627 	 * is not correct.
1628 	 */
1629 	ASSERT(rg->gindex != 0);
1630 
1631 	nxge = rg->nxgep;
1632 	vr = shp->vrp;
1633 
1634 	switch (rg->type) {
1635 	case MAC_RING_TYPE_RX:
1636 		/*
1637 		 * Make sure that the group has the right rings associated
1638 		 * for the share. In version 1.0, we may only give a VR
1639 		 * 2 RDCs.  Not only that, but the HV has statically
1640 		 * assigned the channels like so:
1641 		 * VR0: RDC0 & RDC1
1642 		 * VR1: RDC2 & RDC3, etc.
1643 		 */
1644 		group = nxge->rx_set.group[rg->gindex];
1645 
1646 		if (group->count > 2) {
1647 			/* a share can have at most 2 rings */
1648 			return (EINVAL);
1649 		}
1650 
1651 		for (i = 0; i < NXGE_MAX_RDCS; i++) {
1652 			if (group->map & (1 << i)) {
1653 				if ((i != shp->index * 2) &&
1654 				    (i != (shp->index * 2 + 1))) {
1655 					/*
1656 					 * A group with invalid rings was
1657 					 * attempted to bind to this share
1658 					 */
1659 					return (EINVAL);
1660 				}
1661 			}
1662 		}
1663 
1664 		rg->sindex = vr->region;
1665 		vr->rdc_tbl = rg->rdctbl;
1666 		shp->rxgroup = vr->rdc_tbl;
1667 		break;
1668 
1669 	case MAC_RING_TYPE_TX:
1670 		/*
1671 		 * Make sure that the group has the right rings associated
1672 		 * for the share. In version 1.0, we may only give a VR
1673 		 * 2 TDCs.  Not only that, but the HV has statically
1674 		 * assigned the channels like so:
1675 		 * VR0: TDC0 & TDC1
1676 		 * VR1: TDC2 & TDC3, etc.
1677 		 */
1678 		group = nxge->tx_set.group[rg->gindex];
1679 
1680 		if (group->count > 2) {
1681 			/* a share can have at most 2 rings */
1682 			return (EINVAL);
1683 		}
1684 
1685 		for (i = 0; i < NXGE_MAX_TDCS; i++) {
1686 			if (group->map & (1 << i)) {
1687 				if ((i != shp->index * 2) &&
1688 				    (i != (shp->index * 2 + 1))) {
1689 					/*
1690 					 * A group with invalid rings was
1691 					 * attempted to bind to this share
1692 					 */
1693 					return (EINVAL);
1694 				}
1695 			}
1696 		}
1697 
1698 		vr->tdc_tbl = nxge->pt_config.hw_config.def_mac_txdma_grpid +
1699 		    rg->gindex;
1700 		rg->sindex = vr->region;
1701 		break;
1702 	}
1703 	return (0);
1704 }
1705 
1706 int
1707 nxge_hio_share_rem_group(mac_share_handle_t shandle,
1708     mac_group_driver_t ghandle)
1709 {
1710 	nxge_share_handle_t	*shp = (nxge_share_handle_t *)shandle;
1711 	nxge_ring_group_t	*group = (nxge_ring_group_t *)ghandle;
1712 	nxge_hio_vr_t		*vr;	/* The Virtualization Region */
1713 	int			rv = 0;
1714 
1715 	vr = shp->vrp;
1716 
1717 	switch (group->type) {
1718 	case MAC_RING_TYPE_RX:
1719 		group->sindex = 0;
1720 		vr->rdc_tbl = 0;
1721 		shp->rxgroup = 0;
1722 		break;
1723 
1724 	case MAC_RING_TYPE_TX:
1725 		group->sindex = 0;
1726 		vr->tdc_tbl = 0;
1727 		break;
1728 	}
1729 
1730 	return (rv);
1731 }
1732 
1733 int
1734 nxge_hio_share_bind(mac_share_handle_t shandle, uint64_t cookie,
1735     uint64_t *rcookie)
1736 {
1737 	nxge_t			*nxge;
1738 	nxge_share_handle_t	*shp = (nxge_share_handle_t *)shandle;
1739 	nxge_hio_vr_t		*vr;
1740 	uint64_t		rmap, tmap, hv_rmap, hv_tmap;
1741 	int			rv;
1742 
1743 	nxge = shp->nxgep;
1744 	vr = (nxge_hio_vr_t *)shp->vrp;
1745 
1746 	/*
1747 	 * Add resources to the share.
1748 	 * For each DMA channel associated with the VR, bind its resources
1749 	 * to the VR.
1750 	 */
1751 	tmap = 0;
1752 	rv = nxge_hio_addres(vr, MAC_RING_TYPE_TX, &tmap);
1753 	if (rv != 0) {
1754 		return (rv);
1755 	}
1756 
1757 	rmap = 0;
1758 	rv = nxge_hio_addres(vr, MAC_RING_TYPE_RX, &rmap);
1759 	if (rv != 0) {
1760 		nxge_hio_remres(vr, MAC_RING_TYPE_TX, tmap);
1761 		return (rv);
1762 	}
1763 
1764 	/*
1765 	 * Ask the Hypervisor to set up the VR and allocate slots for
1766 	 * each rings associated with the VR.
1767 	 */
1768 	hv_tmap = hv_rmap = 0;
1769 	if ((rv = nxge_hio_share_assign(nxge, cookie,
1770 	    &hv_tmap, &hv_rmap, vr))) {
1771 		nxge_hio_remres(vr, MAC_RING_TYPE_TX, tmap);
1772 		nxge_hio_remres(vr, MAC_RING_TYPE_RX, rmap);
1773 		return (rv);
1774 	}
1775 
1776 	shp->active = B_TRUE;
1777 	shp->tmap = hv_tmap;
1778 	shp->rmap = hv_rmap;
1779 
1780 	/* high 32 bits are cfg_hdl and low 32 bits are HV cookie */
1781 	*rcookie = (((uint64_t)nxge->niu_cfg_hdl) << 32) | vr->cookie;
1782 
1783 	return (0);
1784 }
1785 
1786 void
1787 nxge_hio_share_unbind(mac_share_handle_t shandle)
1788 {
1789 	nxge_share_handle_t *shp = (nxge_share_handle_t *)shandle;
1790 
1791 	/*
1792 	 * First, unassign the VR (take it back),
1793 	 * so we can enable interrupts again.
1794 	 */
1795 	nxge_hio_share_unassign(shp->vrp);
1796 
1797 	/*
1798 	 * Free Ring Resources for TX and RX
1799 	 */
1800 	nxge_hio_remres(shp->vrp, MAC_RING_TYPE_TX, shp->tmap);
1801 	nxge_hio_remres(shp->vrp, MAC_RING_TYPE_RX, shp->rmap);
1802 }
1803 
1804 
1805 /*
1806  * nxge_hio_vr_share
1807  *
1808  *	Find an unused Virtualization Region (VR).
1809  *
1810  * Arguments:
1811  * 	nxge
1812  *
1813  * Notes:
1814  *
1815  * Context:
1816  *	Service domain
1817  */
1818 nxge_hio_vr_t *
1819 nxge_hio_vr_share(
1820 	nxge_t *nxge)
1821 {
1822 	nxge_hio_data_t *nhd = (nxge_hio_data_t *)nxge->nxge_hw_p->hio;
1823 	nxge_hio_vr_t *vr;
1824 
1825 	int first, limit, region;
1826 
1827 	NXGE_DEBUG_MSG((nxge, HIO_CTL, "==> nxge_hio_vr_share"));
1828 
1829 	MUTEX_ENTER(&nhd->lock);
1830 
1831 	if (nhd->vrs == 0) {
1832 		MUTEX_EXIT(&nhd->lock);
1833 		return (0);
1834 	}
1835 
1836 	/* Find an empty virtual region (VR). */
1837 	if (nxge->function_num == 0) {
1838 		// FUNC0_VIR0 'belongs' to NIU port 0.
1839 		first = FUNC0_VIR1;
1840 		limit = FUNC2_VIR0;
1841 	} else if (nxge->function_num == 1) {
1842 		// FUNC2_VIR0 'belongs' to NIU port 1.
1843 		first = FUNC2_VIR1;
1844 		limit = FUNC_VIR_MAX;
1845 	} else {
1846 		cmn_err(CE_WARN,
1847 		    "Shares not supported on function(%d) at this time.\n",
1848 		    nxge->function_num);
1849 	}
1850 
1851 	for (region = first; region < limit; region++) {
1852 		if (nhd->vr[region].nxge == 0)
1853 			break;
1854 	}
1855 
1856 	if (region == limit) {
1857 		MUTEX_EXIT(&nhd->lock);
1858 		return (0);
1859 	}
1860 
1861 	vr = &nhd->vr[region];
1862 	vr->nxge = (uintptr_t)nxge;
1863 	vr->region = (uintptr_t)region;
1864 
1865 	nhd->vrs--;
1866 
1867 	MUTEX_EXIT(&nhd->lock);
1868 
1869 	NXGE_DEBUG_MSG((nxge, HIO_CTL, "<== nxge_hio_vr_share"));
1870 
1871 	return (vr);
1872 }
1873 
1874 void
1875 nxge_hio_unshare(
1876 	nxge_hio_vr_t *vr)
1877 {
1878 	nxge_t *nxge = (nxge_t *)vr->nxge;
1879 	nxge_hio_data_t *nhd;
1880 
1881 	vr_region_t region;
1882 
1883 	NXGE_DEBUG_MSG((nxge, HIO_CTL, "==> nxge_hio_unshare"));
1884 
1885 	if (!nxge) {
1886 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL, "nxge_hio_unshare: "
1887 		    "vr->nxge is NULL"));
1888 		return;
1889 	}
1890 
1891 	/*
1892 	 * This function is no longer called, but I will keep it
1893 	 * here in case we want to revisit this topic in the future.
1894 	 *
1895 	 * nxge_hio_hostinfo_uninit(nxge, vr);
1896 	 */
1897 
1898 	/*
1899 	 * XXX: This is done by ms_sremove?
1900 	 * (void) nxge_fzc_rdc_tbl_unbind(nxge, vr->rdc_tbl);
1901 	 */
1902 
1903 	nhd = (nxge_hio_data_t *)nxge->nxge_hw_p->hio;
1904 
1905 	MUTEX_ENTER(&nhd->lock);
1906 
1907 	region = vr->region;
1908 	(void) memset(vr, 0, sizeof (*vr));
1909 	vr->region = region;
1910 
1911 	nhd->vrs++;
1912 
1913 	MUTEX_EXIT(&nhd->lock);
1914 
1915 	NXGE_DEBUG_MSG((nxge, HIO_CTL, "<== nxge_hio_unshare"));
1916 }
1917 
1918 int
1919 nxge_hio_addres(
1920 	nxge_hio_vr_t *vr,
1921 	mac_ring_type_t type,
1922 	uint64_t *map)
1923 {
1924 	nxge_t		*nxge = (nxge_t *)vr->nxge;
1925 	nxge_grp_t	*group;
1926 	int		groupid;
1927 	int		i;
1928 	int		max_dcs;
1929 
1930 	NXGE_DEBUG_MSG((nxge, HIO_CTL, "==> nxge_hio_addres"));
1931 
1932 	if (!nxge)
1933 		return (EINVAL);
1934 
1935 	/*
1936 	 * For each ring associated with the group, add the resources
1937 	 * to the group and bind.
1938 	 */
1939 	max_dcs = (type == MAC_RING_TYPE_TX) ? NXGE_MAX_TDCS : NXGE_MAX_RDCS;
1940 	if (type == MAC_RING_TYPE_TX) {
1941 		/* set->group is an array of group indexed by a port group id */
1942 		groupid = vr->tdc_tbl -
1943 		    nxge->pt_config.hw_config.def_mac_txdma_grpid;
1944 		group = nxge->tx_set.group[groupid];
1945 	} else {
1946 		/* set->group is an array of group indexed by a port group id */
1947 		groupid = vr->rdc_tbl -
1948 		    nxge->pt_config.hw_config.def_mac_rxdma_grpid;
1949 		group = nxge->rx_set.group[groupid];
1950 	}
1951 
1952 	if (group->map == 0) {
1953 		NXGE_DEBUG_MSG((nxge, HIO_CTL, "There is no rings associated "
1954 		    "with this VR"));
1955 		return (EINVAL);
1956 	}
1957 
1958 	for (i = 0; i < max_dcs; i++) {
1959 		if (group->map & (1 << i)) {
1960 			int rv;
1961 
1962 			if ((rv = nxge_hio_dc_share(nxge, vr, type, i)) < 0) {
1963 				if (*map == 0) /* Couldn't get even one DC. */
1964 					return (-rv);
1965 				else
1966 					break;
1967 			}
1968 			*map |= (1 << i);
1969 		}
1970 	}
1971 
1972 	NXGE_DEBUG_MSG((nxge, HIO_CTL, "<== nxge_hio_addres"));
1973 
1974 	return (0);
1975 }
1976 
1977 /* ARGSUSED */
1978 void
1979 nxge_hio_remres(
1980 	nxge_hio_vr_t *vr,
1981 	mac_ring_type_t type,
1982 	res_map_t res_map)
1983 {
1984 	nxge_t *nxge = (nxge_t *)vr->nxge;
1985 	nxge_grp_t *group;
1986 
1987 	if (!nxge) {
1988 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL, "nxge_hio_remres: "
1989 		    "vr->nxge is NULL"));
1990 		return;
1991 	}
1992 
1993 	NXGE_DEBUG_MSG((nxge, HIO_CTL, "==> nxge_hio_remres(%lx)", res_map));
1994 
1995 	/*
1996 	 * For each ring bound to the group, remove the DMA resources
1997 	 * from the group and unbind.
1998 	 */
1999 	group = (type == MAC_RING_TYPE_TX ? &vr->tx_group : &vr->rx_group);
2000 	while (group->dc) {
2001 		nxge_hio_dc_t *dc = group->dc;
2002 		NXGE_DC_RESET(res_map, dc->page);
2003 		nxge_hio_dc_unshare(nxge, vr, type, dc->channel);
2004 	}
2005 
2006 	if (res_map) {
2007 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL, "nxge_hio_remres: "
2008 		    "res_map %lx", res_map));
2009 	}
2010 
2011 	NXGE_DEBUG_MSG((nxge, HIO_CTL, "<== nxge_hio_remres"));
2012 }
2013 
2014 /*
2015  * nxge_hio_tdc_share
2016  *
2017  *	Share an unused TDC channel.
2018  *
2019  * Arguments:
2020  * 	nxge
2021  *
2022  * Notes:
2023  *
2024  * A.7.3 Reconfigure Tx DMA channel
2025  *	Disable TxDMA			A.9.6.10
2026  *     [Rebind TxDMA channel to Port	A.9.6.7]
2027  *
2028  * We don't have to Rebind the TDC to the port - it always already bound.
2029  *
2030  *	Soft Reset TxDMA		A.9.6.2
2031  *
2032  * This procedure will be executed by nxge_init_txdma_channel() in the
2033  * guest domain:
2034  *
2035  *	Re-initialize TxDMA		A.9.6.8
2036  *	Reconfigure TxDMA
2037  *	Enable TxDMA			A.9.6.9
2038  *
2039  * Context:
2040  *	Service domain
2041  */
2042 int
2043 nxge_hio_tdc_share(
2044 	nxge_t *nxge,
2045 	int channel)
2046 {
2047 	nxge_hio_data_t *nhd = (nxge_hio_data_t *)nxge->nxge_hw_p->hio;
2048 	nxge_grp_set_t *set = &nxge->tx_set;
2049 	tx_ring_t *ring;
2050 	int count;
2051 
2052 	NXGE_DEBUG_MSG((nxge, HIO_CTL, "==> nxge_hio_tdc_share"));
2053 
2054 	/*
2055 	 * Wait until this channel is idle.
2056 	 */
2057 	ring = nxge->tx_rings->rings[channel];
2058 
2059 	(void) atomic_swap_32(&ring->tx_ring_offline, NXGE_TX_RING_OFFLINING);
2060 	if (ring->tx_ring_busy) {
2061 		/*
2062 		 * Wait for 30 seconds.
2063 		 */
2064 		for (count = 30 * 1000; count; count--) {
2065 			if (ring->tx_ring_offline & NXGE_TX_RING_OFFLINED) {
2066 				break;
2067 			}
2068 
2069 			drv_usecwait(1000);
2070 		}
2071 
2072 		if (count == 0) {
2073 			(void) atomic_swap_32(&ring->tx_ring_offline,
2074 			    NXGE_TX_RING_ONLINE);
2075 			NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
2076 			    "nxge_hio_tdc_share: "
2077 			    "Tx ring %d was always BUSY", channel));
2078 			return (-EIO);
2079 		}
2080 	} else {
2081 		(void) atomic_swap_32(&ring->tx_ring_offline,
2082 		    NXGE_TX_RING_OFFLINED);
2083 	}
2084 
2085 	MUTEX_ENTER(&nhd->lock);
2086 	nxge->tdc_is_shared[channel] = B_TRUE;
2087 	MUTEX_EXIT(&nhd->lock);
2088 
2089 	if (nxge_intr_remove(nxge, VP_BOUND_TX, channel) != NXGE_OK) {
2090 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL, "nxge_hio_tdc_share: "
2091 		    "Failed to remove interrupt for TxDMA channel %d",
2092 		    channel));
2093 		return (-EINVAL);
2094 	}
2095 
2096 	/* Disable TxDMA A.9.6.10 */
2097 	(void) nxge_txdma_channel_disable(nxge, channel);
2098 
2099 	/* The SD is sharing this channel. */
2100 	NXGE_DC_SET(set->shared.map, channel);
2101 	set->shared.count++;
2102 
2103 	/* Soft Reset TxDMA A.9.6.2 */
2104 	nxge_grp_dc_remove(nxge, VP_BOUND_TX, channel);
2105 
2106 	/*
2107 	 * Initialize the DC-specific FZC control registers.
2108 	 * -----------------------------------------------------
2109 	 */
2110 	if (nxge_init_fzc_tdc(nxge, channel) != NXGE_OK) {
2111 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
2112 		    "nxge_hio_tdc_share: FZC TDC failed: %d", channel));
2113 		return (-EIO);
2114 	}
2115 
2116 	NXGE_DEBUG_MSG((nxge, HIO_CTL, "<== nxge_hio_tdc_share"));
2117 
2118 	return (0);
2119 }
2120 
2121 /*
2122  * nxge_hio_rdc_share
2123  *
2124  *	Share an unused RDC channel.
2125  *
2126  * Arguments:
2127  * 	nxge
2128  *
2129  * Notes:
2130  *
2131  * This is the latest version of the procedure to
2132  * Reconfigure an Rx DMA channel:
2133  *
2134  * A.6.3 Reconfigure Rx DMA channel
2135  *	Stop RxMAC		A.9.2.6
2136  *	Drain IPP Port		A.9.3.6
2137  *	Stop and reset RxDMA	A.9.5.3
2138  *
2139  * This procedure will be executed by nxge_init_rxdma_channel() in the
2140  * guest domain:
2141  *
2142  *	Initialize RxDMA	A.9.5.4
2143  *	Reconfigure RxDMA
2144  *	Enable RxDMA		A.9.5.5
2145  *
2146  * We will do this here, since the RDC is a canalis non grata:
2147  *	Enable RxMAC		A.9.2.10
2148  *
2149  * Context:
2150  *	Service domain
2151  */
2152 int
2153 nxge_hio_rdc_share(
2154 	nxge_t *nxge,
2155 	nxge_hio_vr_t *vr,
2156 	int channel)
2157 {
2158 	nxge_grp_set_t *set = &nxge->rx_set;
2159 	nxge_rdc_grp_t *rdc_grp;
2160 
2161 	NXGE_DEBUG_MSG((nxge, HIO_CTL, "==> nxge_hio_rdc_share"));
2162 
2163 	/* Disable interrupts. */
2164 	if (nxge_intr_remove(nxge, VP_BOUND_RX, channel) != NXGE_OK) {
2165 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL, "nxge_hio_rdc_share: "
2166 		    "Failed to remove interrupt for RxDMA channel %d",
2167 		    channel));
2168 		return (NXGE_ERROR);
2169 	}
2170 
2171 	/* Stop RxMAC = A.9.2.6 */
2172 	if (nxge_rx_mac_disable(nxge) != NXGE_OK) {
2173 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL, "nxge_hio_rdc_share: "
2174 		    "Failed to disable RxMAC"));
2175 	}
2176 
2177 	/* Drain IPP Port = A.9.3.6 */
2178 	(void) nxge_ipp_drain(nxge);
2179 
2180 	/* Stop and reset RxDMA = A.9.5.3 */
2181 	// De-assert EN: RXDMA_CFIG1[31] = 0 (DMC+00000 )
2182 	if (nxge_disable_rxdma_channel(nxge, channel) != NXGE_OK) {
2183 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL, "nxge_hio_rdc_share: "
2184 		    "Failed to disable RxDMA channel %d", channel));
2185 	}
2186 
2187 	/* The SD is sharing this channel. */
2188 	NXGE_DC_SET(set->shared.map, channel);
2189 	set->shared.count++;
2190 
2191 	// Assert RST: RXDMA_CFIG1[30] = 1
2192 	nxge_grp_dc_remove(nxge, VP_BOUND_RX, channel);
2193 
2194 	/*
2195 	 * The guest domain will reconfigure the RDC later.
2196 	 *
2197 	 * But in the meantime, we must re-enable the Rx MAC so
2198 	 * that we can start receiving packets again on the
2199 	 * remaining RDCs:
2200 	 *
2201 	 * Enable RxMAC = A.9.2.10
2202 	 */
2203 	if (nxge_rx_mac_enable(nxge) != NXGE_OK) {
2204 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
2205 		    "nxge_hio_rdc_share: Rx MAC still disabled"));
2206 	}
2207 
2208 	/*
2209 	 * Initialize the DC-specific FZC control registers.
2210 	 * -----------------------------------------------------
2211 	 */
2212 	if (nxge_init_fzc_rdc(nxge, channel) != NXGE_OK) {
2213 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
2214 		    "nxge_hio_rdc_share: RZC RDC failed: %ld", channel));
2215 		return (-EIO);
2216 	}
2217 
2218 	/*
2219 	 * We have to initialize the guest's RDC table, too.
2220 	 * -----------------------------------------------------
2221 	 */
2222 	rdc_grp = &nxge->pt_config.rdc_grps[vr->rdc_tbl];
2223 	if (rdc_grp->max_rdcs == 0) {
2224 		rdc_grp->start_rdc = (uint8_t)channel;
2225 		rdc_grp->def_rdc = (uint8_t)channel;
2226 		rdc_grp->max_rdcs = 1;
2227 	} else {
2228 		rdc_grp->max_rdcs++;
2229 	}
2230 	NXGE_DC_SET(rdc_grp->map, channel);
2231 
2232 	NXGE_DEBUG_MSG((nxge, HIO_CTL, "<== nxge_hio_rdc_share"));
2233 
2234 	return (0);
2235 }
2236 
2237 /*
2238  * nxge_hio_dc_share
2239  *
2240  *	Share a DMA channel with a guest domain.
2241  *
2242  * Arguments:
2243  * 	nxge
2244  * 	vr	The VR that <channel> will belong to.
2245  * 	type	Tx or Rx.
2246  * 	channel	Channel to share
2247  *
2248  * Notes:
2249  *
2250  * Context:
2251  *	Service domain
2252  */
2253 int
2254 nxge_hio_dc_share(
2255 	nxge_t *nxge,
2256 	nxge_hio_vr_t *vr,
2257 	mac_ring_type_t type,
2258 	int channel)
2259 {
2260 	nxge_hio_data_t *nhd = (nxge_hio_data_t *)nxge->nxge_hw_p->hio;
2261 	nxge_hio_dc_t *dc;
2262 	nxge_grp_t *group;
2263 	int slot;
2264 
2265 	NXGE_DEBUG_MSG((nxge, HIO_CTL, "==> nxge_hio_dc_share(%cdc %d",
2266 	    type == MAC_RING_TYPE_TX ? 't' : 'r', channel));
2267 
2268 
2269 	/* -------------------------------------------------- */
2270 	slot = (type == MAC_RING_TYPE_TX) ?
2271 	    nxge_hio_tdc_share(nxge, channel) :
2272 	    nxge_hio_rdc_share(nxge, vr, channel);
2273 
2274 	if (slot < 0) {
2275 		if (type == MAC_RING_TYPE_RX) {
2276 			nxge_hio_rdc_unshare(nxge, vr->rdc_tbl, channel);
2277 		} else {
2278 			nxge_hio_tdc_unshare(nxge, vr->tdc_tbl, channel);
2279 		}
2280 		return (slot);
2281 	}
2282 
2283 	MUTEX_ENTER(&nhd->lock);
2284 
2285 	/*
2286 	 * Tag this channel.
2287 	 * --------------------------------------------------
2288 	 */
2289 	dc = type == MAC_RING_TYPE_TX ? &nhd->tdc[channel] : &nhd->rdc[channel];
2290 
2291 	dc->vr = vr;
2292 	dc->channel = (nxge_channel_t)channel;
2293 
2294 	MUTEX_EXIT(&nhd->lock);
2295 
2296 	/*
2297 	 * vr->[t|r]x_group is used by the service domain to
2298 	 * keep track of its shared DMA channels.
2299 	 */
2300 	MUTEX_ENTER(&nxge->group_lock);
2301 	group = (type == MAC_RING_TYPE_TX ? &vr->tx_group : &vr->rx_group);
2302 
2303 	dc->group = group;
2304 	/* Initialize <group>, if necessary */
2305 	if (group->count == 0) {
2306 		group->nxge = nxge;
2307 		group->type = (type == MAC_RING_TYPE_TX) ?
2308 		    VP_BOUND_TX : VP_BOUND_RX;
2309 		group->sequence	= nhd->sequence++;
2310 		group->active = B_TRUE;
2311 	}
2312 
2313 	MUTEX_EXIT(&nxge->group_lock);
2314 
2315 	NXGE_ERROR_MSG((nxge, HIO_CTL,
2316 	    "DC share: %cDC %d was assigned to slot %d",
2317 	    type == MAC_RING_TYPE_TX ? 'T' : 'R', channel, slot));
2318 
2319 	nxge_grp_dc_append(nxge, group, dc);
2320 
2321 	NXGE_DEBUG_MSG((nxge, HIO_CTL, "<== nxge_hio_dc_share"));
2322 
2323 	return (0);
2324 }
2325 
2326 /*
2327  * nxge_hio_tdc_unshare
2328  *
2329  *	Unshare a TDC.
2330  *
2331  * Arguments:
2332  * 	nxge
2333  * 	channel	The channel to unshare (add again).
2334  *
2335  * Notes:
2336  *
2337  * Context:
2338  *	Service domain
2339  */
2340 void
2341 nxge_hio_tdc_unshare(
2342 	nxge_t *nxge,
2343 	int dev_grpid,
2344 	int channel)
2345 {
2346 	nxge_grp_set_t *set = &nxge->tx_set;
2347 	nxge_grp_t *group;
2348 	int grpid;
2349 
2350 	NXGE_DEBUG_MSG((nxge, HIO_CTL, "==> nxge_hio_tdc_unshare"));
2351 
2352 	NXGE_DC_RESET(set->shared.map, channel);
2353 	set->shared.count--;
2354 
2355 	grpid = dev_grpid - nxge->pt_config.hw_config.def_mac_txdma_grpid;
2356 	group = set->group[grpid];
2357 
2358 	if ((nxge_grp_dc_add(nxge, group, VP_BOUND_TX, channel))) {
2359 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL, "nxge_hio_tdc_unshare: "
2360 		    "Failed to initialize TxDMA channel %d", channel));
2361 		return;
2362 	}
2363 
2364 	/* Re-add this interrupt. */
2365 	if (nxge_intr_add(nxge, VP_BOUND_TX, channel) != NXGE_OK) {
2366 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL, "nxge_hio_tdc_unshare: "
2367 		    "Failed to add interrupt for TxDMA channel %d", channel));
2368 	}
2369 
2370 	NXGE_DEBUG_MSG((nxge, HIO_CTL, "<== nxge_hio_tdc_unshare"));
2371 }
2372 
2373 /*
2374  * nxge_hio_rdc_unshare
2375  *
2376  *	Unshare an RDC: add it to the SD's RDC groups (tables).
2377  *
2378  * Arguments:
2379  * 	nxge
2380  * 	channel	The channel to unshare (add again).
2381  *
2382  * Notes:
2383  *
2384  * Context:
2385  *	Service domain
2386  */
2387 void
2388 nxge_hio_rdc_unshare(
2389 	nxge_t *nxge,
2390 	int dev_grpid,
2391 	int channel)
2392 {
2393 	nxge_grp_set_t		*set = &nxge->rx_set;
2394 	nxge_grp_t		*group;
2395 	int			grpid;
2396 
2397 	NXGE_DEBUG_MSG((nxge, HIO_CTL, "==> nxge_hio_rdc_unshare"));
2398 
2399 	/* Stop RxMAC = A.9.2.6 */
2400 	if (nxge_rx_mac_disable(nxge) != NXGE_OK) {
2401 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL, "nxge_hio_rdc_unshare: "
2402 		    "Failed to disable RxMAC"));
2403 	}
2404 
2405 	/* Drain IPP Port = A.9.3.6 */
2406 	(void) nxge_ipp_drain(nxge);
2407 
2408 	/* Stop and reset RxDMA = A.9.5.3 */
2409 	// De-assert EN: RXDMA_CFIG1[31] = 0 (DMC+00000 )
2410 	if (nxge_disable_rxdma_channel(nxge, channel) != NXGE_OK) {
2411 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL, "nxge_hio_rdc_unshare: "
2412 		    "Failed to disable RxDMA channel %d", channel));
2413 	}
2414 
2415 	NXGE_DC_RESET(set->shared.map, channel);
2416 	set->shared.count--;
2417 
2418 	grpid = dev_grpid - nxge->pt_config.hw_config.def_mac_rxdma_grpid;
2419 	group = set->group[grpid];
2420 
2421 	/*
2422 	 * Assert RST: RXDMA_CFIG1[30] = 1
2423 	 *
2424 	 * Initialize RxDMA	A.9.5.4
2425 	 * Reconfigure RxDMA
2426 	 * Enable RxDMA		A.9.5.5
2427 	 */
2428 	if ((nxge_grp_dc_add(nxge, group, VP_BOUND_RX, channel))) {
2429 		/* Be sure to re-enable the RX MAC. */
2430 		if (nxge_rx_mac_enable(nxge) != NXGE_OK) {
2431 			NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
2432 			    "nxge_hio_rdc_share: Rx MAC still disabled"));
2433 		}
2434 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL, "nxge_hio_rdc_unshare: "
2435 		    "Failed to initialize RxDMA channel %d", channel));
2436 		return;
2437 	}
2438 
2439 	/*
2440 	 * Enable RxMAC = A.9.2.10
2441 	 */
2442 	if (nxge_rx_mac_enable(nxge) != NXGE_OK) {
2443 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
2444 		    "nxge_hio_rdc_share: Rx MAC still disabled"));
2445 		return;
2446 	}
2447 
2448 	/* Re-add this interrupt. */
2449 	if (nxge_intr_add(nxge, VP_BOUND_RX, channel) != NXGE_OK) {
2450 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
2451 		    "nxge_hio_rdc_unshare: Failed to add interrupt for "
2452 		    "RxDMA CHANNEL %d", channel));
2453 	}
2454 
2455 	NXGE_DEBUG_MSG((nxge, HIO_CTL, "<== nxge_hio_rdc_unshare"));
2456 }
2457 
2458 /*
2459  * nxge_hio_dc_unshare
2460  *
2461  *	Unshare (reuse) a DMA channel.
2462  *
2463  * Arguments:
2464  * 	nxge
2465  * 	vr	The VR that <channel> belongs to.
2466  * 	type	Tx or Rx.
2467  * 	channel	The DMA channel to reuse.
2468  *
2469  * Notes:
2470  *
2471  * Context:
2472  *	Service domain
2473  */
2474 void
2475 nxge_hio_dc_unshare(
2476 	nxge_t *nxge,
2477 	nxge_hio_vr_t *vr,
2478 	mac_ring_type_t type,
2479 	int channel)
2480 {
2481 	nxge_grp_t *group;
2482 	nxge_hio_dc_t *dc;
2483 
2484 	NXGE_DEBUG_MSG((nxge, HIO_CTL, "==> nxge_hio_dc_unshare(%cdc %d)",
2485 	    type == MAC_RING_TYPE_TX ? 't' : 'r', channel));
2486 
2487 	/* Unlink the channel from its group. */
2488 	/* -------------------------------------------------- */
2489 	group = (type == MAC_RING_TYPE_TX) ? &vr->tx_group : &vr->rx_group;
2490 	NXGE_DC_RESET(group->map, channel);
2491 	if ((dc = nxge_grp_dc_unlink(nxge, group, channel)) == 0) {
2492 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
2493 		    "nxge_hio_dc_unshare(%d) failed", channel));
2494 		return;
2495 	}
2496 
2497 	dc->vr = 0;
2498 	dc->cookie = 0;
2499 
2500 	if (type == MAC_RING_TYPE_RX) {
2501 		nxge_hio_rdc_unshare(nxge, vr->rdc_tbl, channel);
2502 	} else {
2503 		nxge_hio_tdc_unshare(nxge, vr->tdc_tbl, channel);
2504 	}
2505 
2506 	NXGE_DEBUG_MSG((nxge, HIO_CTL, "<== nxge_hio_dc_unshare"));
2507 }
2508 
2509 
2510 /*
2511  * nxge_hio_rxdma_bind_intr():
2512  *
2513  *	For the guest domain driver, need to bind the interrupt group
2514  *	and state to the rx_rcr_ring_t.
2515  */
2516 
2517 int
2518 nxge_hio_rxdma_bind_intr(nxge_t *nxge, rx_rcr_ring_t *ring, int channel)
2519 {
2520 	nxge_hio_dc_t	*dc;
2521 	nxge_ldgv_t	*control;
2522 	nxge_ldg_t	*group;
2523 	nxge_ldv_t	*device;
2524 
2525 	/*
2526 	 * Find the DMA channel.
2527 	 */
2528 	if (!(dc = nxge_grp_dc_find(nxge, VP_BOUND_RX, channel))) {
2529 		return (NXGE_ERROR);
2530 	}
2531 
2532 	/*
2533 	 * Get the control structure.
2534 	 */
2535 	control = nxge->ldgvp;
2536 	if (control == NULL) {
2537 		return (NXGE_ERROR);
2538 	}
2539 
2540 	group = &control->ldgp[dc->ldg.vector];
2541 	device = &control->ldvp[dc->ldg.ldsv];
2542 
2543 	MUTEX_ENTER(&ring->lock);
2544 	ring->ldgp = group;
2545 	ring->ldvp = device;
2546 	MUTEX_EXIT(&ring->lock);
2547 
2548 	return (NXGE_OK);
2549 }
2550 #endif	/* if defined(sun4v) */
2551