nxge_hio.c revision e11f08147bbd4f6060125c2163dc2493ca18b82f
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
*
* This file manages the virtualization resources for Neptune
* devices. That is, it implements a hybrid I/O (HIO) approach in the
* Solaris kernel, whereby a guest domain on an LDOMs server may
* request & use hardware resources from the service domain.
*
*/
#define NXGE_HIO_SHARE_MIN_CHANNELS 2
#define NXGE_HIO_SHARE_MAX_CHANNELS 2
/*
* External prototypes
*/
/* The following function may be found in nxge_main.c */
/* The following function may be found in nxge_[t|r]xdma.c */
/*
* Local prototypes
*/
/*
* These functions are used by both service & guest domains to
* or not. If so, then the Hybrid I/O (HIO) module is initialized.
*/
/*
* nxge_get_environs
*
* Figure out if we are in a guest domain or not.
*
* Arguments:
* nxge
*
* Notes:
*
* Context:
* Any domain
*/
void
{
char *string;
/*
*/
/*
* Are we a hybrid I/O (HIO) guest domain driver?
*/
/* So we can allocate properly-aligned memory. */
"Hybrid IO-capable guest domain"));
}
}
}
#if !defined(sun4v)
/*
* nxge_hio_init
*
* Initialize the HIO module of the NXGE driver.
*
* Arguments:
* nxge
*
* Notes:
* This is the non-hybrid I/O version of this function.
*
* Context:
* Any domain
*/
int
{
if (nhd == 0) {
}
return (NXGE_OK);
}
#endif
void
{
}
}
/*
* nxge_dci_map
*
* Map a DMA channel index to a channel number.
*
* Arguments:
* instance The instance number of the driver.
* type The type of channel this is: Tx or Rx.
* index The index to convert to a channel number
*
* Notes:
* This function is called by nxge_ndd.c:nxge_param_set_port_rdc()
*
* Context:
* Any domain
*/
int
int index)
{
int dc;
switch (type) {
case VP_BOUND_TX:
break;
case VP_BOUND_RX:
break;
}
if (index == 0)
return (dc);
else
index--;
}
}
return (-1);
}
/*
* ---------------------------------------------------------------------
* These are the general-purpose DMA channel group functions. That is,
* these functions are used to manage groups of TDCs or RDCs in an HIO
* environment.
*
* But is also expected that in the future they will be able to manage
* Crossbow groups.
* ---------------------------------------------------------------------
*/
/*
* nxge_grp_cleanup(p_nxge_t nxge)
*
* Remove all outstanding groups.
*
* Arguments:
* nxge
*/
void
{
int i;
/*
* Find RX groups that need to be cleaned up.
*/
for (i = 0; i < NXGE_LOGICAL_GROUP_MAX; i++) {
}
}
/*
* Find TX groups that need to be cleaned up.
*/
for (i = 0; i < NXGE_LOGICAL_GROUP_MAX; i++) {
}
}
}
/*
* nxge_grp_add
*
* Add a group to an instance of NXGE.
*
* Arguments:
* nxge
* type Tx or Rx
*
* Notes:
*
* Context:
* Any domain
*/
{
int i;
switch (type) {
case NXGE_TRANSMIT_GROUP:
case EXT_TRANSMIT_GROUP:
break;
default:
break;
}
/* Find an empty slot for this logical group. */
for (i = 0; i < NXGE_LOGICAL_GROUP_MAX; i++) {
break;
}
}
"nxge_grp_add: %cgroup = %d.%d",
return (group);
}
void
{
case NXGE_TRANSMIT_GROUP:
case EXT_TRANSMIT_GROUP:
break;
default:
break;
}
return;
}
/* While inside the mutex, deactivate <group>. */
"nxge_grp_remove(%c.%d.%d) called",
/* Now, remove any DCs which are still active. */
default:
type = VP_BOUND_TX;
break;
case NXGE_RECEIVE_GROUP:
case EXT_RECEIVE_GROUP:
type = VP_BOUND_RX;
}
}
}
/*
* nxge_grp_dc_add
*
*
* Arguments:
* nxge
* channel The channel to add.
* Notes:
*
* Context:
* Any domain
*/
/* ARGSUSED */
int
{
return (0);
switch (type) {
case VP_BOUND_TX:
if (channel > NXGE_MAX_TDCS) {
"nxge_grp_dc_add: TDC = %d", channel));
return (NXGE_ERROR);
}
break;
case VP_BOUND_RX:
if (channel > NXGE_MAX_RDCS) {
"nxge_grp_dc_add: RDC = %d", channel));
return (NXGE_ERROR);
}
break;
default:
"nxge_grp_dc_add: unknown type channel(%d)", channel));
return (NXGE_ERROR);
}
"nxge_grp_dc_add: %cgroup = %d.%d.%d, channel = %d",
/* We may be in the process of removing this group. */
return (NXGE_ERROR);
}
"nxge_grp_dc_add(%d): DC FIND failed", channel));
return (NXGE_ERROR);
}
/* This channel is already in use! */
"nxge_grp_dc_add(%d): channel already in group", channel));
return (NXGE_ERROR);
}
if (type == VP_BOUND_RX) {
} else {
}
if (isLDOMguest(nxge))
"nxge_grp_dc_add(%d): channel init failed", channel));
return (NXGE_ERROR);
}
if (type == VP_BOUND_TX) {
}
return ((int)status);
}
void
int channel)
{
goto nxge_grp_dc_remove_exit;
goto nxge_grp_dc_remove_exit;
}
if (isLDOMguest(nxge)) {
}
"DC remove: group = %d.%d.%d, %cdc %d",
}
/* Remove the DC from its group. */
"nxge_grp_dc_remove(%d) failed", channel));
goto nxge_grp_dc_remove_exit;
}
}
int channel)
{
if (!isLDOMguest(nxge)) {
} else {
/* We're in a guest domain. */
return (current);
}
}
}
}
return (0);
}
/*
* nxge_grp_dc_append
*
* Append a DMA channel to a group.
*
* Arguments:
* nxge
* group The group to append to
* dc The DMA channel to append
*
* Notes:
*
* Context:
* Any domain
*/
static
void
{
} else {
do {
break;
}
} while (current);
}
}
/*
* nxge_grp_dc_unlink
*
* Unlink a DMA channel fromits linked list (group).
*
* Arguments:
* nxge
* group The group (linked list) to unlink from
* dc The DMA channel to append
*
* Notes:
*
* Context:
* Any domain
*/
{
return (0);
}
return (0);
}
previous = 0;
do {
if (previous)
else
break;
}
} while (current);
if (current == 0) {
"DC unlink: DC %d not found", channel));
} else {
}
return (current);
}
/*
* nxge_grp_dc_map
*
* Map a linked list to an array of channel numbers.
*
* Arguments:
* nxge
* group The group to remap.
*
* Notes:
* It is expected that the caller will hold the correct mutex.
*
* Context:
* Service domain
*/
void
{
while (dc) {
legend++;
}
}
/*
* ---------------------------------------------------------------------
* These are HIO debugging functions.
* ---------------------------------------------------------------------
*/
/*
* nxge_delay
*
* Delay <seconds> number of seconds.
*
* Arguments:
* nxge
* group The group to append to
* dc The DMA channel to append
*
* Notes:
* This is a developer-only function.
*
* Context:
* Any domain
*/
void
int seconds)
{
}
static dmc_reg_name_t rx_names[] = {
{ "RXDMA_CFIG1", 0 },
{ "RXDMA_CFIG2", 8 },
{ "RBR_CFIG_A", 0x10 },
{ "RBR_CFIG_B", 0x18 },
{ "RBR_KICK", 0x20 },
{ "RBR_STAT", 0x28 },
{ "RBR_HDH", 0x30 },
{ "RBR_HDL", 0x38 },
{ "RCRCFIG_A", 0x40 },
{ "RCRCFIG_B", 0x48 },
{ "RCRSTAT_A", 0x50 },
{ "RCRSTAT_B", 0x58 },
{ "RCRSTAT_C", 0x60 },
{ "RX_DMA_ENT_MSK", 0x68 },
{ "RX_DMA_CTL_STAT", 0x70 },
{ "RCR_FLSH", 0x78 },
{ "RXMISC", 0x90 },
{ "RX_DMA_CTL_STAT_DBG", 0x98 },
{ 0, -1 }
};
static dmc_reg_name_t tx_names[] = {
{ "Tx_RNG_CFIG", 0 },
{ "Tx_RNG_HDL", 0x10 },
{ "Tx_RNG_KICK", 0x18 },
{ "Tx_ENT_MASK", 0x20 },
{ "Tx_CS", 0x28 },
{ "TxDMA_MBH", 0x30 },
{ "TxDMA_MBL", 0x38 },
{ "TxDMA_PRE_ST", 0x40 },
{ "Tx_RNG_ERR_LOGH", 0x48 },
{ "Tx_RNG_ERR_LOGL", 0x50 },
{ "TDMC_INTR_DBG", 0x60 },
{ "Tx_CS_DBG", 0x68 },
{ 0, -1 }
};
/*
* nxge_xx2str
*
* Translate a register address into a string.
*
* Arguments:
* offset The address of the register to translate.
*
* Notes:
* These are developer-only function.
*
* Context:
* Any domain
*/
const char *
int offset)
{
offset &= DMA_CSR_MASK;
reg++;
}
return (0);
}
const char *
int offset)
{
offset &= DMA_CSR_MASK;
reg++;
}
return (0);
}
/*
* nxge_ddi_perror
*
* Map a DDI error number to a string.
*
* Arguments:
* ddi_error The DDI error number to map.
*
* Notes:
*
* Context:
* Any domain
*/
const char *
int ddi_error)
{
switch (ddi_error) {
case DDI_SUCCESS:
return ("DDI_SUCCESS");
case DDI_FAILURE:
return ("DDI_FAILURE");
case DDI_NOT_WELL_FORMED:
return ("DDI_NOT_WELL_FORMED");
case DDI_EAGAIN:
return ("DDI_EAGAIN");
case DDI_EINVAL:
return ("DDI_EINVAL");
case DDI_ENOTSUP:
return ("DDI_ENOTSUP");
case DDI_EPENDING:
return ("DDI_EPENDING");
case DDI_ENOMEM:
return ("DDI_ENOMEM");
case DDI_EBUSY:
return ("DDI_EBUSY");
case DDI_ETRANSPORT:
return ("DDI_ETRANSPORT");
case DDI_ECONTEXT:
return ("DDI_ECONTEXT");
default:
return ("Unknown error");
}
}
/*
* ---------------------------------------------------------------------
* These are Sun4v HIO function definitions
* ---------------------------------------------------------------------
*/
#if defined(sun4v)
/*
* Local prototypes
*/
static void nxge_hio_unshare(nxge_hio_vr_t *);
mac_ring_type_t, int);
/*
* nxge_hio_init
*
* Initialize the HIO module of the NXGE driver.
*
* Arguments:
* nxge
*
* Notes:
*
* Context:
* Any domain
*/
int
{
int i, region;
if (nhd == 0) {
}
"nxge_hio_init: hypervisor services "
"version %d.%d",
}
}
return (NXGE_OK);
}
/*
* Fill in what we can.
*/
}
/*
* Initialize tdc share state, shares and ring group structures.
*/
for (i = 0; i < NXGE_MAX_TDCS; i++)
for (i = 0; i < NXGE_MAX_RDC_GROUPS; i++) {
}
for (i = 0; i < NXGE_VR_SR_MAX; i++) {
}
/* Fill in the HV HIO function pointers. */
if (isLDOMservice(nxge)) {
"Hybrid IO-capable service domain"));
return (NXGE_OK);
} else {
/*
* isLDOMguest(nxge) == B_TRUE
*/
modgetsymvalue("vio_net_resource_reg", 0);
modgetsymvalue("vio_net_resource_unreg", 0);
return (NXGE_ERROR);
}
}
return (0);
}
static int
{
/*
*/
(ether_addr_t *)mac_addr)) != 0) {
return (rv);
}
return (0);
}
/* ARGSUSED */
static int
{
int sindex;
/*
*/
return (0);
}
/* ARGSUSED */
void
{
switch (type) {
case MAC_RING_TYPE_RX:
break;
case MAC_RING_TYPE_TX:
break;
}
}
int
{
int i;
/*
* Ask the Hypervisor to set up the VR for us
*/
"nxge_hio_share_assign: "
"vr->assign() returned %d", hv_rv));
return (-EIO);
}
/*
* For each shared TDC, ask the HV to find us an empty slot.
* -----------------------------------------------------
*/
for (i = 0; i < NXGE_MAX_TDCS; i++) {
while (dc) {
if (hv_rv != 0) {
"nxge_hio_share_assign: "
"tx->assign(%x, %d) failed: %ld",
return (-EIO);
}
/* Inform the caller about the slot chosen. */
}
}
/*
* For each shared RDC, ask the HV to find us an empty slot.
* -----------------------------------------------------
*/
for (i = 0; i < NXGE_MAX_RDCS; i++) {
while (dc) {
if (hv_rv != 0) {
"nxge_hio_share_assign: "
"rx->assign(%x, %d) failed: %ld",
return (-EIO);
}
/* Inform the caller about the slot chosen. */
}
}
return (0);
}
int
{
while (dc) {
if (hv_rv != 0) {
"nxge_hio_share_unassign: "
"tx->unassign(%x, %d) failed: %ld",
}
}
while (dc) {
if (hv_rv != 0) {
"nxge_hio_share_unassign: "
"rx->unassign(%x, %d) failed: %ld",
}
}
if (hv_rv != 0) {
"nxge_hio_share_unassign: "
"vr->assign(%x) failed: %ld",
}
}
return (0);
}
int
{
int rv;
return (EIO);
}
/*
* Get a VR.
*/
return (EAGAIN);
/*
* Get an RDC group for us to use.
*/
return (EBUSY);
}
/*
* Add resources to the share.
*/
tmap = 0;
if (rv != 0) {
return (rv);
}
rmap = 0;
if (rv != 0) {
return (rv);
}
return (rv);
}
/* high 32 bits are cfg_hdl and low 32 bits are HV cookie */
return (0);
}
void
{
/*
* First, unassign the VR (take it back),
* so we can enable interrupts again.
*/
/*
* Free Ring Resources for TX and RX
*/
/*
* Free VR resource.
*/
/*
* Clear internal handle state.
*/
}
void
{
switch (type) {
case MAC_RING_TYPE_RX:
break;
case MAC_RING_TYPE_TX:
*gnum = 0;
break;
}
}
/*
* nxge_hio_vr_share
*
* Find an unused Virtualization Region (VR).
*
* Arguments:
* nxge
*
* Notes:
*
* Context:
* Service domain
*/
{
return (0);
}
/* Find an empty virtual region (VR). */
if (nxge->function_num == 0) {
// FUNC0_VIR0 'belongs' to NIU port 0.
first = FUNC0_VIR1;
limit = FUNC2_VIR0;
// FUNC2_VIR0 'belongs' to NIU port 1.
first = FUNC2_VIR1;
} else {
"Shares not supported on function(%d) at this time.\n",
nxge->function_num);
}
break;
}
return (0);
}
return (vr);
}
void
{
if (!nxge) {
"vr->nxge is NULL"));
return;
}
/*
* This function is no longer called, but I will keep it
* here in case we want to revisit this topic in the future.
*
* nxge_hio_hostinfo_uninit(nxge, vr);
*/
}
int
int count)
{
int i;
if (!nxge)
return (EINVAL);
for (i = 0; i < count; i++) {
int rv;
if (i == 0) /* Couldn't get even one DC. */
return (-rv);
else
break;
}
}
return (0);
}
/* ARGSUSED */
void
{
if (!nxge) {
"vr->nxge is NULL"));
return;
}
}
if (res_map) {
"res_map %lx", res_map));
}
}
/*
* nxge_hio_tdc_share
*
* Share an unused TDC channel.
*
* Arguments:
* nxge
*
* Notes:
*
* A.7.3 Reconfigure Tx DMA channel
* Disable TxDMA A.9.6.10
* [Rebind TxDMA channel to Port A.9.6.7]
*
* We don't have to Rebind the TDC to the port - it always already bound.
*
* Soft Reset TxDMA A.9.6.2
*
* This procedure will be executed by nxge_init_txdma_channel() in the
* guest domain:
*
* Re-initialize TxDMA A.9.6.8
* Reconfigure TxDMA
* Enable TxDMA A.9.6.9
*
* Context:
* Service domain
*/
int
int channel)
{
int count;
/*
* Wait until this channel is idle.
*/
if (ring->tx_ring_busy) {
/*
* Wait for 30 seconds.
*/
break;
}
drv_usecwait(1000);
}
if (count == 0) {
"nxge_hio_tdc_share: "
"Tx ring %d was always BUSY", channel));
return (-EIO);
}
} else {
}
"Failed to remove interrupt for TxDMA channel %d",
channel));
return (NXGE_ERROR);
}
/* Disable TxDMA A.9.6.10 */
/* The SD is sharing this channel. */
/* Soft Reset TxDMA A.9.6.2 */
/*
* Initialize the DC-specific FZC control registers.
* -----------------------------------------------------
*/
"nxge_hio_tdc_share: FZC TDC failed: %d", channel));
return (-EIO);
}
return (0);
}
/*
* nxge_hio_rdc_share
*
* Share an unused RDC channel.
*
* Arguments:
* nxge
*
* Notes:
*
* This is the latest version of the procedure to
* Reconfigure an Rx DMA channel:
*
* A.6.3 Reconfigure Rx DMA channel
* Stop RxMAC A.9.2.6
* Drain IPP Port A.9.3.6
* Stop and reset RxDMA A.9.5.3
*
* This procedure will be executed by nxge_init_rxdma_channel() in the
* guest domain:
*
* Initialize RxDMA A.9.5.4
* Reconfigure RxDMA
* Enable RxDMA A.9.5.5
*
* We will do this here, since the RDC is a canalis non grata:
* Enable RxMAC A.9.2.10
*
* Context:
* Service domain
*/
int
int channel)
{
/* Disable interrupts. */
"Failed to remove interrupt for RxDMA channel %d",
channel));
return (NXGE_ERROR);
}
/* Stop RxMAC = A.9.2.6 */
"Failed to disable RxMAC"));
}
/* Drain IPP Port = A.9.3.6 */
(void) nxge_ipp_drain(nxge);
/* Stop and reset RxDMA = A.9.5.3 */
// De-assert EN: RXDMA_CFIG1[31] = 0 (DMC+00000 )
"Failed to disable RxDMA channel %d", channel));
}
/* The SD is sharing this channel. */
// Assert RST: RXDMA_CFIG1[30] = 1
/*
* We have to reconfigure the RDC table(s)
* to which this channel belongs.
*/
}
}
/*
* The guest domain will reconfigure the RDC later.
*
* But in the meantime, we must re-enable the Rx MAC so
* that we can start receiving packets again on the
* remaining RDCs:
*
* Enable RxMAC = A.9.2.10
*/
"nxge_hio_rdc_share: Rx MAC still disabled"));
}
/*
* Initialize the DC-specific FZC control registers.
* -----------------------------------------------------
*/
"nxge_hio_rdc_share: RZC RDC failed: %ld", channel));
return (-EIO);
}
/*
* We have to initialize the guest's RDC table, too.
* -----------------------------------------------------
*/
} else {
}
"nxge_hio_rdc_share: nxge_init_fzc_rdc_tbl failed"));
return (-EIO);
}
return (0);
}
/*
* nxge_hio_dc_share
*
* Share a DMA channel with a guest domain.
*
* Arguments:
* nxge
* vr The VR that <channel> will belong to.
* type Tx or Rx.
* res_map The resource map used by the caller, which we will
* update if successful.
*
* Notes:
*
* Context:
* Service domain
*/
int
{
int slot;
/*
* In version 1.0, we may only give a VR 2 RDCs or TDCs.
* Not only that, but the HV has statically assigned the
* channels like so:
* VR0: RDC0 & RDC1
* VR1: RDC2 & RDC3, etc.
* The TDCs are assigned in exactly the same way.
*
* So, for example
* hardware->start_rdc + vr->region * 2;
* VR1: hardware->start_rdc + 1 * 2;
* VR3: hardware->start_rdc + 3 * 2;
* If start_rdc is 0, we end up with 2 or 6.
* If start_rdc is 8, we end up with 10 or 14.
*/
// This code is still NIU-specific (assuming only 2 ports)
break;
}
}
"nxge_hio_dc_share: there are no channels to share"));
return (-EIO);
}
/* -------------------------------------------------- */
if (slot < 0) {
if (type == MAC_RING_TYPE_RX) {
} else {
}
return (slot);
}
/*
* Tag this channel.
* --------------------------------------------------
*/
/*
* vr->[t|r]x_group is used by the service domain to
* keep track of its shared DMA channels.
*/
/* Initialize <group>, if necessary */
}
"DC share: %cDC %d was assigned to slot %d",
return (0);
}
/*
* nxge_hio_tdc_unshare
*
* Unshare a TDC.
*
* Arguments:
* nxge
* channel The channel to unshare (add again).
*
* Notes:
*
* Context:
* Service domain
*/
void
int channel)
{
"Failed to initialize TxDMA channel %d", channel));
return;
}
/* Re-add this interrupt. */
"Failed to add interrupt for TxDMA channel %d", channel));
}
}
/*
* nxge_hio_rdc_unshare
*
* Unshare an RDC: add it to the SD's RDC groups (tables).
*
* Arguments:
* nxge
* channel The channel to unshare (add again).
*
* Notes:
*
* Context:
* Service domain
*/
void
int channel)
{
/* Stop RxMAC = A.9.2.6 */
"Failed to disable RxMAC"));
}
/* Drain IPP Port = A.9.3.6 */
(void) nxge_ipp_drain(nxge);
/* Stop and reset RxDMA = A.9.5.3 */
// De-assert EN: RXDMA_CFIG1[31] = 0 (DMC+00000 )
"Failed to disable RxDMA channel %d", channel));
}
/*
* Assert RST: RXDMA_CFIG1[30] = 1
*
* Initialize RxDMA A.9.5.4
* Reconfigure RxDMA
* Enable RxDMA A.9.5.5
*/
/* Be sure to re-enable the RX MAC. */
"nxge_hio_rdc_unshare: Rx MAC still disabled"));
}
"Failed to initialize RxDMA channel %d", channel));
return;
}
/*
* We have to reconfigure the RDC table(s)
* to which this channel once again belongs.
*/
}
}
/*
* Enable RxMAC = A.9.2.10
*/
"nxge_hio_rdc_unshare: Rx MAC still disabled"));
return;
}
/* Re-add this interrupt. */
"nxge_hio_rdc_unshare: Failed to add interrupt for "
"RxDMA CHANNEL %d", channel));
}
}
/*
* nxge_hio_dc_unshare
*
* Unshare (reuse) a DMA channel.
*
* Arguments:
* nxge
* vr The VR that <channel> belongs to.
* type Tx or Rx.
* channel The DMA channel to reuse.
*
* Notes:
*
* Context:
* Service domain
*/
void
int channel)
{
/* Unlink the channel from its group. */
/* -------------------------------------------------- */
"nxge_hio_dc_unshare(%d) failed", channel));
return;
}
if (type == MAC_RING_TYPE_RX) {
} else {
}
}
#endif /* if defined(sun4v) */