/* * Copyright (C) 2017 Michael Brown . * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * You can also choose to distribute this program under the terms of * the Unmodified Binary Distribution Licence (as given in the file * COPYING.UBDL), provided that you have satisfied its requirements. */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include #include #include #include #include #include #include #include #include #include #include #include "exanic.h" /** @file * * Exablaze ExaNIC driver * */ /* Disambiguate the various error causes */ #define EIO_ABORTED __einfo_error ( EINFO_EIO_ABORTED ) #define EINFO_EIO_ABORTED \ __einfo_uniqify ( EINFO_EIO, 0x01, "Frame aborted" ) #define EIO_CORRUPT __einfo_error ( EINFO_EIO_CORRUPT ) #define EINFO_EIO_CORRUPT \ __einfo_uniqify ( EINFO_EIO, 0x02, "CRC incorrect" ) #define EIO_HWOVFL __einfo_error ( EINFO_EIO_HWOVFL ) #define EINFO_EIO_HWOVFL \ __einfo_uniqify ( EINFO_EIO, 0x03, "Hardware overflow" ) #define EIO_STATUS( status ) \ EUNIQ ( EINFO_EIO, ( (status) & EXANIC_STATUS_ERROR_MASK ), \ EIO_ABORTED, EIO_CORRUPT, EIO_HWOVFL ) /** * Write DMA base address register * * @v addr DMA base address * @v reg Register */ static void exanic_write_base ( physaddr_t addr, void *reg ) { uint32_t lo; uint32_t hi; /* Write high and low registers, setting flags as appropriate */ lo = addr; if ( sizeof ( physaddr_t ) > sizeof ( uint32_t ) ) { /* 64-bit build; may be a 32-bit or 64-bit address */ hi = ( ( ( uint64_t ) addr ) >> 32 ); if ( ! hi ) lo |= EXANIC_DMA_32_BIT; } else { /* 32-bit build; always a 32-bit address */ hi = 0; lo |= EXANIC_DMA_32_BIT; } writel ( hi, ( reg + 0 ) ); writel ( lo, ( reg + 4 ) ); } /** * Clear DMA base address register * * @v reg Register */ static inline void exanic_clear_base ( void *reg ) { /* Clear both high and low registers */ writel ( 0, ( reg + 0 ) ); writel ( 0, ( reg + 4 ) ); } /****************************************************************************** * * Device reset * ****************************************************************************** */ /** * Reset hardware * * @v exanic ExaNIC device */ static void exanic_reset ( struct exanic *exanic ) { void *port_regs; unsigned int i; /* Disable all possible ports */ for ( i = 0 ; i < EXANIC_MAX_PORTS ; i++ ) { port_regs = ( exanic->regs + EXANIC_PORT_REGS ( i ) ); writel ( 0, ( port_regs + EXANIC_PORT_ENABLE ) ); writel ( 0, ( port_regs + EXANIC_PORT_IRQ ) ); exanic_clear_base ( port_regs + EXANIC_PORT_RX_BASE ); } /* Disable transmit feedback */ exanic_clear_base ( exanic->regs + EXANIC_TXF_BASE ); } /****************************************************************************** * * MAC address * ****************************************************************************** */ /** * Read I2C line status * * @v basher Bit-bashing interface * @v bit_id Bit number * @ret zero Input is a logic 0 * @ret non-zero Input is a logic 1 */ static int exanic_i2c_read_bit ( struct bit_basher *basher, unsigned int bit_id ) { struct exanic *exanic = container_of ( basher, struct exanic, basher.basher ); unsigned int shift; uint32_t i2c; /* Identify bit */ assert ( bit_id == I2C_BIT_SDA ); shift = exanic->i2cfg.getsda; /* Read I2C register */ DBG_DISABLE ( DBGLVL_IO ); i2c = readl ( exanic->regs + EXANIC_I2C ); DBG_ENABLE ( DBGLVL_IO ); return ( ( i2c >> shift ) & 1 ); } /** * Write I2C line status * * @v basher Bit-bashing interface * @v bit_id Bit number * @v data Value to write */ static void exanic_i2c_write_bit ( struct bit_basher *basher, unsigned int bit_id, unsigned long data ) { struct exanic *exanic = container_of ( basher, struct exanic, basher.basher ); unsigned int shift; uint32_t mask; uint32_t i2c; /* Identify shift */ assert ( ( bit_id == I2C_BIT_SCL ) || ( bit_id == I2C_BIT_SDA ) ); shift = ( ( bit_id == I2C_BIT_SCL ) ? exanic->i2cfg.setscl : exanic->i2cfg.setsda ); mask = ( 1UL << shift ); /* Modify I2C register */ DBG_DISABLE ( DBGLVL_IO ); i2c = readl ( exanic->regs + EXANIC_I2C ); i2c &= ~mask; if ( ! data ) i2c |= mask; writel ( i2c, ( exanic->regs + EXANIC_I2C ) ); DBG_ENABLE ( DBGLVL_IO ); } /** I2C bit-bashing interface operations */ static struct bit_basher_operations exanic_i2c_basher_ops = { .read = exanic_i2c_read_bit, .write = exanic_i2c_write_bit, }; /** Possible I2C bus configurations */ static struct exanic_i2c_config exanic_i2cfgs[] = { /* X2/X10 */ { .setscl = 7, .setsda = 4, .getsda = 12 }, /* X4 */ { .setscl = 7, .setsda = 5, .getsda = 13 }, }; /** * Initialise EEPROM * * @v exanic ExaNIC device * @v i2cfg I2C bus configuration * @ret rc Return status code */ static int exanic_try_init_eeprom ( struct exanic *exanic, struct exanic_i2c_config *i2cfg ) { int rc; /* Configure I2C bus */ memcpy ( &exanic->i2cfg, i2cfg, sizeof ( exanic->i2cfg ) ); /* Initialise I2C bus */ if ( ( rc = init_i2c_bit_basher ( &exanic->basher, &exanic_i2c_basher_ops ) ) != 0 ) { DBGC2 ( exanic, "EXANIC %p found no I2C bus via %d/%d/%d\n", exanic, exanic->i2cfg.setscl, exanic->i2cfg.setsda, exanic->i2cfg.getsda ); return rc; } /* Check for EEPROM presence */ init_i2c_eeprom ( &exanic->eeprom, EXANIC_EEPROM_ADDRESS ); if ( ( rc = i2c_check_presence ( &exanic->basher.i2c, &exanic->eeprom ) ) != 0 ) { DBGC2 ( exanic, "EXANIC %p found no EEPROM via %d/%d/%d\n", exanic, exanic->i2cfg.setscl, exanic->i2cfg.setsda, exanic->i2cfg.getsda ); return rc; } DBGC ( exanic, "EXANIC %p found EEPROM via %d/%d/%d\n", exanic, exanic->i2cfg.setscl, exanic->i2cfg.setsda, exanic->i2cfg.getsda ); return 0; } /** * Initialise EEPROM * * @v exanic ExaNIC device * @ret rc Return status code */ static int exanic_init_eeprom ( struct exanic *exanic ) { struct exanic_i2c_config *i2cfg; unsigned int i; int rc; /* Try all possible bus configurations */ for ( i = 0 ; i < ( sizeof ( exanic_i2cfgs ) / sizeof ( exanic_i2cfgs[0] ) ) ; i++ ) { i2cfg = &exanic_i2cfgs[i]; if ( ( rc = exanic_try_init_eeprom ( exanic, i2cfg ) ) == 0 ) return 0; } DBGC ( exanic, "EXANIC %p found no EEPROM\n", exanic ); return -ENODEV; } /** * Fetch base MAC address * * @v exanic ExaNIC device * @ret rc Return status code */ static int exanic_fetch_mac ( struct exanic *exanic ) { struct i2c_interface *i2c = &exanic->basher.i2c; int rc; /* Initialise EEPROM */ if ( ( rc = exanic_init_eeprom ( exanic ) ) != 0 ) return rc; /* Fetch base MAC address */ if ( ( rc = i2c->read ( i2c, &exanic->eeprom, 0, exanic->mac, sizeof ( exanic->mac ) ) ) != 0 ) { DBGC ( exanic, "EXANIC %p could not read MAC address: %s\n", exanic, strerror ( rc ) ); return rc; } return 0; } /****************************************************************************** * * Link state * ****************************************************************************** */ /** * Check link state * * @v netdev Network device */ static void exanic_check_link ( struct net_device *netdev ) { struct exanic_port *port = netdev->priv; uint32_t status; uint32_t speed; /* Report port status changes */ status = readl ( port->regs + EXANIC_PORT_STATUS ); speed = readl ( port->regs + EXANIC_PORT_SPEED ); if ( status != port->status ) { DBGC ( port, "EXANIC %s port status %#08x speed %dMbps\n", netdev->name, status, speed ); if ( status & EXANIC_PORT_STATUS_LINK ) { netdev_link_up ( netdev ); } else { netdev_link_down ( netdev ); } port->status = status; } } /** * Check link state periodically * * @v retry Link state check timer * @v over Failure indicator */ static void exanic_expired ( struct retry_timer *timer, int over __unused ) { struct exanic_port *port = container_of ( timer, struct exanic_port, timer ); struct net_device *netdev = port->netdev; static const uint32_t speeds[] = { 100, 1000, 10000, 40000, 100000, }; unsigned int index; /* Restart timer */ start_timer_fixed ( timer, EXANIC_LINK_INTERVAL ); /* Check link state */ exanic_check_link ( netdev ); /* Do nothing further if link is already up */ if ( netdev_link_ok ( netdev ) ) return; /* Do nothing further unless we have a valid list of supported speeds */ if ( ! port->speeds ) return; /* Autonegotiation is not supported; try manually selecting * the next supported link speed. */ do { if ( ! port->speed ) port->speed = ( 8 * sizeof ( port->speeds ) ); port->speed--; } while ( ! ( ( 1UL << port->speed ) & port->speeds ) ); index = ( port->speed - ( ffs ( EXANIC_CAPS_SPEED_MASK ) - 1 ) ); assert ( index < ( sizeof ( speeds ) / sizeof ( speeds[0] ) ) ); /* Attempt the selected speed */ DBGC ( netdev, "EXANIC %s attempting %dMbps\n", netdev->name, speeds[index] ); writel ( speeds[index], ( port->regs + EXANIC_PORT_SPEED ) ); } /****************************************************************************** * * Network device interface * ****************************************************************************** */ /** * Open network device * * @v netdev Network device * @ret rc Return status code */ static int exanic_open ( struct net_device *netdev ) { struct exanic_port *port = netdev->priv; struct exanic_tx_chunk *tx; unsigned int i; /* Reset transmit region contents */ for ( i = 0 ; i < port->tx_count ; i++ ) { tx = ( port->tx + ( i * sizeof ( *tx ) ) ); writew ( port->txf_slot, &tx->desc.txf_slot ); writeb ( EXANIC_TYPE_RAW, &tx->desc.type ); writeb ( 0, &tx->desc.flags ); writew ( 0, &tx->pad ); } /* Reset receive region contents */ memset_user ( port->rx, 0, 0xff, EXANIC_RX_LEN ); /* Reset transmit feedback region */ *(port->txf) = 0; /* Reset counters */ port->tx_prod = 0; port->tx_cons = 0; port->rx_cons = 0; /* Map receive region */ exanic_write_base ( phys_to_bus ( user_to_phys ( port->rx, 0 ) ), ( port->regs + EXANIC_PORT_RX_BASE ) ); /* Enable promiscuous mode */ writel ( EXANIC_PORT_FLAGS_PROMISC, ( port->regs + EXANIC_PORT_FLAGS ) ); /* Reset to default speed and clear cached status */ writel ( port->default_speed, ( port->regs + EXANIC_PORT_SPEED ) ); port->speed = 0; port->status = 0; /* Enable port */ wmb(); writel ( EXANIC_PORT_ENABLE_ENABLED, ( port->regs + EXANIC_PORT_ENABLE ) ); /* Start link state timer */ start_timer_fixed ( &port->timer, EXANIC_LINK_INTERVAL ); return 0; } /** * Close network device * * @v netdev Network device */ static void exanic_close ( struct net_device *netdev ) { struct exanic_port *port = netdev->priv; /* Stop link state timer */ stop_timer ( &port->timer ); /* Disable port */ writel ( 0, ( port->regs + EXANIC_PORT_ENABLE ) ); wmb(); /* Clear receive region */ exanic_clear_base ( port->regs + EXANIC_PORT_RX_BASE ); /* Discard any in-progress receive */ if ( port->rx_iobuf ) { netdev_rx_err ( netdev, port->rx_iobuf, -ECANCELED ); port->rx_iobuf = NULL; } } /** * Transmit packet * * @v netdev Network device * @v iobuf I/O buffer * @ret rc Return status code */ static int exanic_transmit ( struct net_device *netdev, struct io_buffer *iobuf ) { struct exanic_port *port = netdev->priv; struct exanic_tx_chunk *tx; unsigned int tx_fill; unsigned int tx_index; size_t offset; size_t len; uint8_t *src; uint8_t *dst; /* Sanity check */ len = iob_len ( iobuf ); if ( len > sizeof ( tx->data ) ) { DBGC ( port, "EXANIC %s transmit too large\n", netdev->name ); return -ENOTSUP; } /* Get next transmit descriptor */ tx_fill = ( port->tx_prod - port->tx_cons ); if ( tx_fill >= port->tx_count ) { DBGC ( port, "EXANIC %s out of transmit descriptors\n", netdev->name ); return -ENOBUFS; } tx_index = ( port->tx_prod & ( port->tx_count - 1 ) ); offset = ( tx_index * sizeof ( *tx ) ); tx = ( port->tx + offset ); DBGC2 ( port, "EXANIC %s TX %04x at [%05zx,%05zx)\n", netdev->name, port->tx_prod, ( port->tx_offset + offset ), ( port->tx_offset + offset + offsetof ( typeof ( *tx ), data ) + len ) ); port->tx_prod++; /* Populate transmit descriptor */ writew ( port->tx_prod, &tx->desc.txf_id ); writew ( ( sizeof ( tx->pad ) + len ), &tx->desc.len ); /* Copy data to transmit region. There is no DMA on the * transmit data path. */ src = iobuf->data; dst = tx->data; while ( len-- ) writeb ( *(src++), dst++ ); /* Send transmit command */ wmb(); writel ( ( port->tx_offset + offset ), ( port->regs + EXANIC_PORT_TX_COMMAND ) ); return 0; } /** * Poll for completed packets * * @v netdev Network device */ static void exanic_poll_tx ( struct net_device *netdev ) { struct exanic_port *port = netdev->priv; /* Report any completed packets */ while ( port->tx_cons != *(port->txf) ) { DBGC2 ( port, "EXANIC %s TX %04x complete\n", netdev->name, port->tx_cons ); netdev_tx_complete_next ( netdev ); port->tx_cons++; } } /** * Poll for received packets * * @v netdev Network device */ static void exanic_poll_rx ( struct net_device *netdev ) { struct exanic_port *port = netdev->priv; struct exanic_rx_chunk *rx; struct exanic_rx_descriptor desc; uint8_t current; uint8_t previous; size_t offset; size_t len; for ( ; ; port->rx_cons++ ) { /* Fetch descriptor */ offset = ( ( port->rx_cons * sizeof ( *rx ) ) % EXANIC_RX_LEN ); copy_from_user ( &desc, port->rx, ( offset + offsetof ( typeof ( *rx ), desc ) ), sizeof ( desc ) ); /* Calculate generation */ current = ( port->rx_cons / ( EXANIC_RX_LEN / sizeof ( *rx ) )); previous = ( current - 1 ); /* Do nothing if no chunk is ready */ if ( desc.generation == previous ) break; /* Allocate I/O buffer if needed */ if ( ! port->rx_iobuf ) { port->rx_iobuf = alloc_iob ( EXANIC_MAX_RX_LEN ); if ( ! port->rx_iobuf ) { /* Wait for next poll */ break; } port->rx_rc = 0; } /* Calculate chunk length */ len = ( desc.len ? desc.len : sizeof ( rx->data ) ); /* Append data to I/O buffer */ if ( len <= iob_tailroom ( port->rx_iobuf ) ) { copy_from_user ( iob_put ( port->rx_iobuf, len ), port->rx, ( offset + offsetof ( typeof ( *rx ), data ) ), len ); } else { DBGC ( port, "EXANIC %s RX too large\n", netdev->name ); port->rx_rc = -ERANGE; } /* Check for overrun */ rmb(); copy_from_user ( &desc.generation, port->rx, ( offset + offsetof ( typeof ( *rx ), desc.generation ) ), sizeof ( desc.generation ) ); if ( desc.generation != current ) { DBGC ( port, "EXANIC %s RX overrun\n", netdev->name ); port->rx_rc = -ENOBUFS; continue; } /* Wait for end of packet */ if ( ! desc.len ) continue; /* Check for receive errors */ if ( desc.status & EXANIC_STATUS_ERROR_MASK ) { port->rx_rc = -EIO_STATUS ( desc.status ); DBGC ( port, "EXANIC %s RX %04x error: %s\n", netdev->name, port->rx_cons, strerror ( port->rx_rc ) ); } else { DBGC2 ( port, "EXANIC %s RX %04x\n", netdev->name, port->rx_cons ); } /* Hand off to network stack */ if ( port->rx_rc ) { netdev_rx_err ( netdev, port->rx_iobuf, port->rx_rc ); } else { iob_unput ( port->rx_iobuf, 4 /* strip CRC */ ); netdev_rx ( netdev, port->rx_iobuf ); } port->rx_iobuf = NULL; } } /** * Poll for completed and received packets * * @v netdev Network device */ static void exanic_poll ( struct net_device *netdev ) { /* Poll for completed packets */ exanic_poll_tx ( netdev ); /* Poll for received packets */ exanic_poll_rx ( netdev ); } /** ExaNIC network device operations */ static struct net_device_operations exanic_operations = { .open = exanic_open, .close = exanic_close, .transmit = exanic_transmit, .poll = exanic_poll, }; /****************************************************************************** * * PCI interface * ****************************************************************************** */ /** * Probe port * * @v exanic ExaNIC device * @v dev Parent device * @v index Port number * @ret rc Return status code */ static int exanic_probe_port ( struct exanic *exanic, struct device *dev, unsigned int index ) { struct net_device *netdev; struct exanic_port *port; void *port_regs; uint32_t status; size_t tx_len; int rc; /* Do nothing if port is not physically present */ port_regs = ( exanic->regs + EXANIC_PORT_REGS ( index ) ); status = readl ( port_regs + EXANIC_PORT_STATUS ); tx_len = readl ( port_regs + EXANIC_PORT_TX_LEN ); if ( ( status & EXANIC_PORT_STATUS_ABSENT ) || ( tx_len == 0 ) ) { rc = 0; goto absent; } /* Allocate network device */ netdev = alloc_etherdev ( sizeof ( *port ) ); if ( ! netdev ) { rc = -ENOMEM; goto err_alloc_netdev; } netdev_init ( netdev, &exanic_operations ); netdev->dev = dev; port = netdev->priv; memset ( port, 0, sizeof ( *port ) ); exanic->port[index] = port; port->netdev = netdev; port->regs = port_regs; timer_init ( &port->timer, exanic_expired, &netdev->refcnt ); /* Identify transmit region */ port->tx_offset = readl ( port->regs + EXANIC_PORT_TX_OFFSET ); if ( tx_len > EXANIC_MAX_TX_LEN ) tx_len = EXANIC_MAX_TX_LEN; assert ( ! ( tx_len & ( tx_len - 1 ) ) ); port->tx = ( exanic->tx + port->tx_offset ); port->tx_count = ( tx_len / sizeof ( struct exanic_tx_chunk ) ); /* Identify transmit feedback region */ port->txf_slot = EXANIC_TXF_SLOT ( index ); port->txf = ( exanic->txf + ( port->txf_slot * sizeof ( *(port->txf) ) ) ); /* Allocate receive region (via umalloc()) */ port->rx = umalloc ( EXANIC_RX_LEN ); if ( ! port->rx ) { rc = -ENOMEM; goto err_alloc_rx; } /* Set MAC address */ memcpy ( netdev->hw_addr, exanic->mac, ETH_ALEN ); netdev->hw_addr[ ETH_ALEN - 1 ] += index; /* Record default link speed and supported speeds */ port->default_speed = readl ( port->regs + EXANIC_PORT_SPEED ); port->speeds = ( exanic->caps & EXANIC_CAPS_SPEED_MASK ); /* Register network device */ if ( ( rc = register_netdev ( netdev ) ) != 0 ) goto err_register_netdev; DBGC ( port, "EXANIC %s port %d TX [%#05zx,%#05zx) TXF %#02x RX " "[%#lx,%#lx)\n", netdev->name, index, port->tx_offset, ( port->tx_offset + tx_len ), port->txf_slot, user_to_phys ( port->rx, 0 ), user_to_phys ( port->rx, EXANIC_RX_LEN ) ); /* Set initial link state */ exanic_check_link ( netdev ); return 0; unregister_netdev ( netdev ); err_register_netdev: ufree ( port->rx ); err_alloc_rx: netdev_nullify ( netdev ); netdev_put ( netdev ); err_alloc_netdev: absent: return rc; } /** * Probe port * * @v exanic ExaNIC device * @v index Port number */ static void exanic_remove_port ( struct exanic *exanic, unsigned int index ) { struct exanic_port *port; /* Do nothing if port is not physically present */ port = exanic->port[index]; if ( ! port ) return; /* Unregister network device */ unregister_netdev ( port->netdev ); /* Free receive region */ ufree ( port->rx ); /* Free network device */ netdev_nullify ( port->netdev ); netdev_put ( port->netdev ); } /** * Probe PCI device * * @v pci PCI device * @ret rc Return status code */ static int exanic_probe ( struct pci_device *pci ) { struct exanic *exanic; unsigned long regs_bar_start; unsigned long tx_bar_start; size_t tx_bar_len; int i; int rc; /* Allocate and initialise structure */ exanic = zalloc ( sizeof ( *exanic ) ); if ( ! exanic ) { rc = -ENOMEM; goto err_alloc; } pci_set_drvdata ( pci, exanic ); /* Fix up PCI device */ adjust_pci_device ( pci ); /* Map registers */ regs_bar_start = pci_bar_start ( pci, EXANIC_REGS_BAR ); exanic->regs = ioremap ( regs_bar_start, EXANIC_REGS_LEN ); if ( ! exanic->regs ) { rc = -ENODEV; goto err_ioremap_regs; } /* Reset device */ exanic_reset ( exanic ); /* Read capabilities */ exanic->caps = readl ( exanic->regs + EXANIC_CAPS ); /* Power up PHYs */ writel ( EXANIC_POWER_ON, ( exanic->regs + EXANIC_POWER ) ); /* Fetch base MAC address */ if ( ( rc = exanic_fetch_mac ( exanic ) ) != 0 ) goto err_fetch_mac; DBGC ( exanic, "EXANIC %p capabilities %#08x base MAC %s\n", exanic, exanic->caps, eth_ntoa ( exanic->mac ) ); /* Map transmit region */ tx_bar_start = pci_bar_start ( pci, EXANIC_TX_BAR ); tx_bar_len = pci_bar_size ( pci, EXANIC_TX_BAR ); exanic->tx = ioremap ( tx_bar_start, tx_bar_len ); if ( ! exanic->tx ) { rc = -ENODEV; goto err_ioremap_tx; } /* Allocate transmit feedback region (shared between all ports) */ exanic->txf = malloc_dma ( EXANIC_TXF_LEN, EXANIC_ALIGN ); if ( ! exanic->txf ) { rc = -ENOMEM; goto err_alloc_txf; } memset ( exanic->txf, 0, EXANIC_TXF_LEN ); exanic_write_base ( virt_to_bus ( exanic->txf ), ( exanic->regs + EXANIC_TXF_BASE ) ); /* Allocate and initialise per-port network devices */ for ( i = 0 ; i < EXANIC_MAX_PORTS ; i++ ) { if ( ( rc = exanic_probe_port ( exanic, &pci->dev, i ) ) != 0 ) goto err_probe_port; } return 0; i = EXANIC_MAX_PORTS; err_probe_port: for ( i-- ; i >= 0 ; i-- ) exanic_remove_port ( exanic, i ); exanic_reset ( exanic ); free_dma ( exanic->txf, EXANIC_TXF_LEN ); err_alloc_txf: iounmap ( exanic->tx ); err_ioremap_tx: iounmap ( exanic->regs ); err_fetch_mac: err_ioremap_regs: free ( exanic ); err_alloc: return rc; } /** * Remove PCI device * * @v pci PCI device */ static void exanic_remove ( struct pci_device *pci ) { struct exanic *exanic = pci_get_drvdata ( pci ); unsigned int i; /* Remove all ports */ for ( i = 0 ; i < EXANIC_MAX_PORTS ; i++ ) exanic_remove_port ( exanic, i ); /* Reset device */ exanic_reset ( exanic ); /* Free transmit feedback region */ free_dma ( exanic->txf, EXANIC_TXF_LEN ); /* Unmap transmit region */ iounmap ( exanic->tx ); /* Unmap registers */ iounmap ( exanic->regs ); /* Free device */ free ( exanic ); } /** ExaNIC PCI device IDs */ static struct pci_device_id exanic_ids[] = { PCI_ROM ( 0x10ee, 0x2b00, "exanic-old", "ExaNIC (old)", 0 ), PCI_ROM ( 0x1ce4, 0x0001, "exanic-x4", "ExaNIC X4", 0 ), PCI_ROM ( 0x1ce4, 0x0002, "exanic-x2", "ExaNIC X2", 0 ), PCI_ROM ( 0x1ce4, 0x0003, "exanic-x10", "ExaNIC X10", 0 ), PCI_ROM ( 0x1ce4, 0x0004, "exanic-x10gm", "ExaNIC X10 GM", 0 ), PCI_ROM ( 0x1ce4, 0x0005, "exanic-x40", "ExaNIC X40", 0 ), PCI_ROM ( 0x1ce4, 0x0006, "exanic-x10hpt", "ExaNIC X10 HPT", 0 ), }; /** ExaNIC PCI driver */ struct pci_driver exanic_driver __pci_driver = { .ids = exanic_ids, .id_count = ( sizeof ( exanic_ids ) / sizeof ( exanic_ids[0] ) ), .probe = exanic_probe, .remove = exanic_remove, };