/*
* 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 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
*
* This file manages the interrupts for a hybrid I/O (hio) device.
* In the future, it may manage interrupts for all Neptune-based
* devices.
*
*/
/*
* External prototypes
*/
/* The following function may be found in nxge_[t|r]xdma.c */
extern uint_t nxge_tx_intr(void *, void *);
extern uint_t nxge_rx_intr(void *, void *);
/*
* Local prototypes
*/
/*
* nxge_intr_add
*
* Add <channel>'s interrupt.
*
* Arguments:
* nxge
* type Tx or Rx
* channel The channel whose interrupt we want to add.
*
* Notes:
* Add here means: add a handler, enable, & arm the interrupt.
*
* Context:
* Service domain
*
*/
int channel)
{
int vector;
"nxge_intr_add(%cDC %d): vector not found", c, channel));
return (NXGE_ERROR);
}
}
!= DDI_SUCCESS) {
"ddi_intr_add_handler(%d) returned %s",
}
interrupts->intr_added++;
/* Enable the interrupt. */
!= DDI_SUCCESS) {
"ddi_intr_enable(%d) returned %s",
}
if (status1 == DDI_SUCCESS) {
/* Finally, arm the interrupt. */
}
}
return (NXGE_OK);
}
/*
* nxge_intr_remove
*
* Remove <channel>'s interrupt.
*
* Arguments:
* nxge
* type Tx or Rx
* channel The channel whose interrupt we want to remove.
*
* Notes:
* Remove here means: disarm, disable, & remove the handler.
*
* Context:
* Service domain
*
*/
int channel)
{
int vector;
"nxge_intr_remove(%cDC %d): vector not found", c, channel));
return (NXGE_ERROR);
}
/* Disarm the interrupt. */
}
/* Disable the interrupt. */
!= DDI_SUCCESS) {
": ddi_intr_disable(%d) returned %s",
}
/* Remove the interrupt handler. */
!= DDI_SUCCESS) {
": ddi_intr_remove_handler(%d) returned %s",
}
if (status1 == DDI_SUCCESS) {
interrupts->intr_added--;
if (interrupts->intr_added == 0)
}
return (NXGE_OK);
}
/*
* nxge_intr_vec_find
*
* Find the interrupt vector associated with <channel>.
*
* Arguments:
* nxge
* type Tx or Rx
* channel The channel whose vector we want to find.
*
* Notes:
*
* Context:
* Service domain
*
*/
static
int
int channel)
{
"==> nxge_intr_vec_find(%cDC %d)",
"nxge_hio_intr_vec_find(%cDC %d): ldgvp == 0",
return (-1);
}
if (type == VP_BOUND_TX) {
} else {
}
break;
}
return (-1);
}
return (vector);
}
/*
* ---------------------------------------------------------------------
* HIO-specific interrupt functions.
* ---------------------------------------------------------------------
*/
/*
* nxge_hio_intr_add
*
* Add <channel>'s interrupt.
*
* Arguments:
* nxge
* type Tx or Rx
* channel The channel whose interrupt we want to remove.
*
* Notes:
*
* Context:
* Guest domain
*
*/
int channel)
{
"==> nxge_hio_intr_add(%cDC %d)", c, channel));
"nxge_hio_intr_add(%cDC %d): ldgvp == 0", c, channel));
return (NXGE_ERROR);
}
"nxge_hio_intr_add: find(%s, %d) failed", c, channel));
return (NXGE_ERROR);
}
/* 'nxge_intr_type' is a bad name for this data structure. */
/* Set <vector> here to make the following code easier to read. */
}
!= DDI_SUCCESS) {
"nxge_hio_intr_add(%cDC %d): "
"ddi_intr_add_handler(%d) returned %s",
return (NXGE_ERROR);
}
interrupts->intr_added++;
/* Enable the interrupt. */
!= DDI_SUCCESS) {
"nxge_hio_intr_add(%cDC %d): "
"ddi_intr_enable(%d) returned %s",
return (NXGE_ERROR);
}
/*
* Note: RDC interrupts will be armed in nxge_m_start(). This
* prevents us from getting an interrupt before we are ready
* to process packets.
*/
if (type == VP_BOUND_TX) {
}
return (NXGE_OK);
}
/*
* nxge_hio_intr_remove
*
* Remove <channel>'s interrupt.
*
* Arguments:
* nxge
* type Tx or Rx
* channel The channel whose interrupt we want to remove.
*
* Notes:
*
* Context:
* Guest domain
*
*/
int channel)
{
"==> nxge_hio_intr_remove(%cDC %d)", c, channel));
"nxge_hio_intr_remove(%cDC %d): ldgvp == 0", c, channel));
return (NXGE_ERROR);
}
"nxge_hio_intr_remove(%cDC %d): DC FIND failed",
c, channel));
return (NXGE_ERROR);
}
"nxge_hio_intr_remove(%cDC %d): interrupting == FALSE",
c, channel));
return (NXGE_OK);
}
/* 'nxge_intr_type' is a bad name for this data structure. */
/* Set <vector> here to make the following code easier to read. */
/* Disarm the interrupt. */
/* Disable the interrupt. */
!= DDI_SUCCESS) {
"nxge_hio_intr_remove(%cDC %d): "
"ddi_intr_disable(%d) returned %s",
}
/* Remove the interrupt handler. */
!= DDI_SUCCESS) {
"nxge_hio_intr_remove(%cDC %d): "
"ddi_intr_remove_handle(%d) returned %s",
}
if (status1 == DDI_SUCCESS) {
interrupts->intr_added--;
if (interrupts->intr_added == 0)
}
return (NXGE_OK);
}
/*
* nxge_hio_intr_init
*
* Initialize interrupts in a guest domain.
*
* Arguments:
* nxge
*
* Notes:
*
* Context:
* Guest domain
*
*/
{
int *prop_val;
int inum = 0;
int i;
/* Look up the "interrupts" property. */
"==> nxge_hio_intr_init(obp): no 'interrupts' property"));
return (NXGE_ERROR);
}
/*
* For each device assigned, the content of each interrupts
* property is its logical device group.
*
* Assignment of interrupts property is in the the following
* order:
*
* two receive channels
* two transmit channels
*/
for (i = 0; i < prop_len; i++) {
"==> nxge_hio_intr_init(obp): F%d: interrupt #%d, ldg %d",
}
hardware->ldg_chn_start = 0;
/* ----------------------------------------------------- */
interrupts->start_inum = 0;
if (ddi_status != DDI_SUCCESS) {
"ddi_intr_get_supported_types() returned 0x%x, "
return (NXGE_ERROR);
}
/* HIOXXX hack */
/* HIOXXX hack */
if (ddi_status != DDI_SUCCESS) {
"ddi_intr_get_navail() returned %s, navail: %d",
"DDI_INTR_NOTFOUND", navail));
return (NXGE_ERROR);
}
"nxge_hio_intr_init: number of available interrupts: %d", navail));
if (ddi_status != DDI_SUCCESS) {
"ddi_intr_get_nintrs() returned %s, nintrs: %d",
"DDI_INTR_NOTFOUND", nintrs));
return (NXGE_ERROR);
}
"nxge_hio_intr_init: number of interrupts: %d", nintrs));
/*
* When <behavior> is set to DDI_INTR_ALLOC_STRICT,
* ddi_intr_alloc() succeeds if and only if <navail>
* interrupts are are allocated. Otherwise, it fails.
*/
if (ddi_status != DDI_SUCCESS) {
"ddi_intr_alloc() returned 0x%x%, "
return (NXGE_ERROR);
}
"nxge_hio_intr_init: number of interrupts allocated: %d", nactual));
/* <ninterrupts> is a dead variable: we may as well use it. */
/* FOI: Get the interrupt priority. */
" ddi_intr_get_pri() failed: %d", ddi_status));
}
/* FOI: Get our interrupt capability flags. */
"ddi_intr_get_cap() failed: %d", ddi_status));
}
"nxge_hio_intr_init: interrupt capabilities: %d",
interrupts->intr_cap));
return (NXGE_OK);
}
/*
* nxge_hio_intr_uninit
*
* Uninitialize interrupts in a guest domain.
*
* Arguments:
* nxge
*
* Notes:
*
* Context:
* Guest domain
*/
void
{
int i;
/* ----------------------------------------------------- */
/*
* If necessary, disable any currently active interrupts.
*/
if (interrupts->intr_enabled) {
int channel;
(void) nxge_hio_intr_remove(
}
}
(void) nxge_hio_intr_remove(
}
}
}
/*
* Free all of our allocated interrupts.
*/
for (i = 0; i < hardware->ninterrupts; i++) {
if (interrupts->htable[i])
interrupts->htable[i] = 0;
}
sizeof (nxge_ldg_t) * NXGE_INT_MAX_LDGS);
}
sizeof (nxge_ldv_t) * NXGE_INT_MAX_LDS);
}
}
/*
* nxge_hio_tdsv_add
*
* Add a transmit device interrupt.
*
* Arguments:
* nxge
* dc The TDC whose interrupt we're adding
*
* Notes:
*
* Context:
* Guest domain
*/
static
{
"nx_hio_tdsv_add: tx->getinfo absent"));
return (EINVAL);
}
/*
* Get the dma channel information.
*/
if (hv_rv != 0) {
"nx_hio_tdsv_add: tx->getinfo failed: %ld", hv_rv));
return (EIO);
}
"nx_hio_tdsv_add: VRgroup = %d, LDSV = %d",
}
/*
* In version 1.0 of the hybrid I/O driver, there
* are eight interrupt vectors per VR.
*
* Vectors 0 - 3 are reserved for RDCs.
* Vectors 4 - 7 are reserved for TDCs.
*/
// Version 1.0 hack only!
return (0);
}
/*
* nxge_hio_rdsv_add
*
* Add a transmit device interrupt.
*
* Arguments:
* nxge
* dc The RDC whose interrupt we're adding
*
* Notes:
*
* Context:
* Guest domain
*/
static
{
"nx_hio_tdsv_add: rx->getinfo absent"));
return (EINVAL);
}
/*
* Get DMA channel information.
*/
if (hv_rv != 0) {
"nx_hio_tdsv_add: rx->getinfo failed: %ld", hv_rv));
return (EIO);
}
"nx_hio_rdsv_add: VRgroup = %d, LDSV = %d",
}
/*
* In version 1.0 of the hybrid I/O driver, there
* are eight interrupt vectors per VR.
*
* Vectors 0 - 3 are reserved for RDCs.
*/
// Version 1.0 hack only!
return (0);
}
/*
* nxge_hio_ldsv_add
*
* Add a transmit or receive interrupt.
*
* Arguments:
* nxge
* dc The DMA channel whose interrupt we're adding
*
* Notes:
* Guest domains can only add interrupts for DMA channels.
* They cannot access the MAC, MIF, or SYSERR interrupts.
*
* Context:
* Guest domain
*/
int
{
return (EIO);
} else {
return (EIO);
}
} else {
}
/*
* Initialize the logical device group data structure first.
*/
/*
* <hw_config.ldg> is a copy of the "interrupts" property.
*/
/*
* Since <vldg_index> is a dead variable, I'm reusing
* it in Hybrid I/O to calculate the offset into the
* virtual PIO_LDSV space.
*/
/*
* <intdata> appears to be a dead variable.
* Though it is not used anywhere in the driver,
* we'll set it anyway.
*/
/*
* Initialize the logical device state vector next.
*/
} else {
}
device->ldv_ldf_masks = 0;
/*
* This code seems to imply a strict 1-to-1 correspondence.
*/
return (0);
}
/*
* nxge_hio_ldsv_im
*
* Manage a VLDG's interrupts.
*
* Arguments:
* nxge
* group The VLDG to manage
*
* Notes:
* There are 8 sets of 4 64-bit registers per VR, 1 per LDG.
* That sums to 256 bytes of virtual PIO_LDSV space.
*
* VLDG0 starts at offset 0,
* VLDG1 starts at offset 32, etc.
*
* Each set consists of 4 registers:
* Logical Device State Vector 0. LDSV0
* Logical Device State Vector 1. LDSV1
* Logical Device State Vector 2. LDSV2
* Logical Device Group Interrupt Management. LDGIMGN
*
* The first three (LDSVx) are read-only. The 4th register is the
* LDGIMGN, the LDG Interrupt Management register, which is used to
* arm the LDG, or set its timer.
*
* The offset to write to is calculated as follows:
*
* 0x2000 + (VLDG << 4) + offset, where:
* VDLG is the virtual group, i.e., index of the LDG.
* offset is the offset (alignment 8) of the register
* to read or write.
*
* So, for example, if we wanted to arm the first TDC of VRx, we would
* calculate the address as:
*
* 0x2000 + (0 << 4) + 0x18 = 0x18
*
* Context:
* Guest domain
*
*/
void
/* Read any register in the PIO_LDSV space. */
{
}
void
/* Write the PIO_LDGIMGN register. */
{
} else {
}
}