summaryrefslogtreecommitdiffstats
path: root/src/drivers/net/intelxl.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/drivers/net/intelxl.c')
-rw-r--r--src/drivers/net/intelxl.c133
1 files changed, 113 insertions, 20 deletions
diff --git a/src/drivers/net/intelxl.c b/src/drivers/net/intelxl.c
index 973f1d91..c98ba265 100644
--- a/src/drivers/net/intelxl.c
+++ b/src/drivers/net/intelxl.c
@@ -118,6 +118,56 @@ static int intelxl_fetch_mac ( struct intelxl_nic *intelxl,
/******************************************************************************
*
+ * MSI-X interrupts
+ *
+ ******************************************************************************
+ */
+
+/**
+ * Enable MSI-X dummy interrupt
+ *
+ * @v intelxl Intel device
+ * @v pci PCI device
+ * @ret rc Return status code
+ */
+int intelxl_msix_enable ( struct intelxl_nic *intelxl,
+ struct pci_device *pci ) {
+ int rc;
+
+ /* Enable MSI-X capability */
+ if ( ( rc = pci_msix_enable ( pci, &intelxl->msix ) ) != 0 ) {
+ DBGC ( intelxl, "INTELXL %p could not enable MSI-X: %s\n",
+ intelxl, strerror ( rc ) );
+ return rc;
+ }
+
+ /* Configure interrupt zero to write to dummy location */
+ pci_msix_map ( &intelxl->msix, 0, virt_to_bus ( &intelxl->msg ), 0 );
+
+ /* Enable dummy interrupt zero */
+ pci_msix_unmask ( &intelxl->msix, 0 );
+
+ return 0;
+}
+
+/**
+ * Disable MSI-X dummy interrupt
+ *
+ * @v intelxl Intel device
+ * @v pci PCI device
+ */
+void intelxl_msix_disable ( struct intelxl_nic *intelxl,
+ struct pci_device *pci ) {
+
+ /* Disable dummy interrupt zero */
+ pci_msix_mask ( &intelxl->msix, 0 );
+
+ /* Disable MSI-X capability */
+ pci_msix_disable ( pci, &intelxl->msix );
+}
+
+/******************************************************************************
+ *
* Admin queue
*
******************************************************************************
@@ -481,6 +531,39 @@ static int intelxl_admin_shutdown ( struct intelxl_nic *intelxl ) {
}
/**
+ * Clear PXE mode
+ *
+ * @v intelxl Intel device
+ * @ret rc Return status code
+ */
+static int intelxl_admin_clear_pxe ( struct intelxl_nic *intelxl ) {
+ struct intelxl_admin_descriptor *cmd;
+ struct intelxl_admin_clear_pxe_params *pxe;
+ uint32_t gllan_rctl_0;
+ int rc;
+
+ /* Do nothing if device is already out of PXE mode */
+ gllan_rctl_0 = readl ( intelxl->regs + INTELXL_GLLAN_RCTL_0 );
+ if ( ! ( gllan_rctl_0 & INTELXL_GLLAN_RCTL_0_PXE_MODE ) ) {
+ DBGC2 ( intelxl, "INTELXL %p already in non-PXE mode\n",
+ intelxl );
+ return 0;
+ }
+
+ /* Populate descriptor */
+ cmd = intelxl_admin_command_descriptor ( intelxl );
+ cmd->opcode = cpu_to_le16 ( INTELXL_ADMIN_CLEAR_PXE );
+ pxe = &cmd->params.pxe;
+ pxe->magic = INTELXL_ADMIN_CLEAR_PXE_MAGIC;
+
+ /* Issue command */
+ if ( ( rc = intelxl_admin_command ( intelxl ) ) != 0 )
+ return rc;
+
+ return 0;
+}
+
+/**
* Get switch configuration
*
* @v intelxl Intel device
@@ -1504,13 +1587,6 @@ static void intelxl_poll_rx ( struct net_device *netdev ) {
void intelxl_poll ( struct net_device *netdev ) {
struct intelxl_nic *intelxl = netdev->priv;
- /* Acknowledge interrupts, if applicable */
- if ( netdev_irq_enabled ( netdev ) ) {
- writel ( ( INTELXL_INT_DYN_CTL_CLEARPBA |
- INTELXL_INT_DYN_CTL_INTENA_MASK ),
- ( intelxl->regs + intelxl->intr ) );
- }
-
/* Poll for completed packets */
intelxl_poll_tx ( netdev );
@@ -1522,19 +1598,23 @@ void intelxl_poll ( struct net_device *netdev ) {
/* Refill RX ring */
intelxl_refill_rx ( intelxl );
-}
-
-/**
- * Enable or disable interrupts
- *
- * @v netdev Network device
- * @v enable Interrupts should be enabled
- */
-static void intelxl_irq ( struct net_device *netdev, int enable ) {
- struct intelxl_nic *intelxl = netdev->priv;
- writel ( ( enable ? INTELXL_INT_DYN_CTL_INTENA : 0 ),
- ( intelxl->regs + intelxl->intr ) );
+ /* Rearm interrupt, since otherwise receive descriptors will
+ * be written back only after a complete cacheline (four
+ * packets) have been received.
+ *
+ * There is unfortunately no efficient way to determine
+ * whether or not rearming the interrupt is necessary. If we
+ * are running inside a hypervisor (e.g. using a VF or PF as a
+ * passed-through PCI device), then the MSI-X write is
+ * redirected by the hypervisor to the real host APIC and the
+ * host ISR then raises an interrupt within the guest. We
+ * therefore cannot poll the nominal MSI-X target location to
+ * watch for the value being written. We could read from the
+ * INT_DYN_CTL register, but this is even less efficient than
+ * just unconditionally rearming the interrupt.
+ */
+ writel ( INTELXL_INT_DYN_CTL_INTENA, intelxl->regs + intelxl->intr );
}
/** Network device operations */
@@ -1543,7 +1623,6 @@ static struct net_device_operations intelxl_operations = {
.close = intelxl_close,
.transmit = intelxl_transmit,
.poll = intelxl_poll,
- .irq = intelxl_irq,
};
/******************************************************************************
@@ -1617,10 +1696,18 @@ static int intelxl_probe ( struct pci_device *pci ) {
if ( ( rc = intelxl_fetch_mac ( intelxl, netdev ) ) != 0 )
goto err_fetch_mac;
+ /* Enable MSI-X dummy interrupt */
+ if ( ( rc = intelxl_msix_enable ( intelxl, pci ) ) != 0 )
+ goto err_msix;
+
/* Open admin queues */
if ( ( rc = intelxl_open_admin ( intelxl ) ) != 0 )
goto err_open_admin;
+ /* Clear PXE mode */
+ if ( ( rc = intelxl_admin_clear_pxe ( intelxl ) ) != 0 )
+ goto err_admin_clear_pxe;
+
/* Get switch configuration */
if ( ( rc = intelxl_admin_switch ( intelxl ) ) != 0 )
goto err_admin_switch;
@@ -1667,8 +1754,11 @@ static int intelxl_probe ( struct pci_device *pci ) {
err_admin_promisc:
err_admin_vsi:
err_admin_switch:
+ err_admin_clear_pxe:
intelxl_close_admin ( intelxl );
err_open_admin:
+ intelxl_msix_disable ( intelxl, pci );
+ err_msix:
err_fetch_mac:
intelxl_reset ( intelxl );
err_reset:
@@ -1695,6 +1785,9 @@ static void intelxl_remove ( struct pci_device *pci ) {
/* Close admin queues */
intelxl_close_admin ( intelxl );
+ /* Disable MSI-X dummy interrupt */
+ intelxl_msix_disable ( intelxl, pci );
+
/* Reset the NIC */
intelxl_reset ( intelxl );