iPXE
Functions | Variables
icplus.c File Reference

IC+ network driver. More...

#include <stdint.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <byteswap.h>
#include <ipxe/netdevice.h>
#include <ipxe/ethernet.h>
#include <ipxe/if_ether.h>
#include <ipxe/iobuf.h>
#include <ipxe/malloc.h>
#include <ipxe/pci.h>
#include "icplus.h"

Go to the source code of this file.

Functions

 FILE_LICENCE (GPL2_OR_LATER_OR_UBDL)
static int icplus_reset (struct icplus_nic *icp)
 Reset hardware.
static int icplus_read_eeprom (struct nvs_device *nvs, unsigned int address, void *data, size_t len)
 Read data from EEPROM.
static int icplus_write_eeprom (struct nvs_device *nvs, unsigned int address __unused, const void *data __unused, size_t len __unused)
 Write data to EEPROM.
static void icplus_init_eeprom (struct icplus_nic *icp)
 Initialise EEPROM.
static int icplus_mii_read_bit (struct bit_basher *basher, unsigned int bit_id)
 Read input bit.
static void icplus_mii_write_bit (struct bit_basher *basher, unsigned int bit_id, unsigned long data)
 Set/clear output bit.
static int icplus_init_phy (struct icplus_nic *icp)
 Configure PHY.
static void icplus_check_link (struct net_device *netdev)
 Check link state.
static void icplus_set_base (struct icplus_nic *icp, unsigned int offset, void *base)
 Set descriptor ring base address.
static int icplus_create_ring (struct icplus_nic *icp, struct icplus_ring *ring)
 Create descriptor ring.
static void icplus_destroy_ring (struct icplus_nic *icp __unused, struct icplus_ring *ring)
 Destroy descriptor ring.
void icplus_refill_rx (struct icplus_nic *icp)
 Refill receive descriptor ring.
static int icplus_open (struct net_device *netdev)
 Open network device.
static void icplus_close (struct net_device *netdev)
 Close network device.
static int icplus_transmit (struct net_device *netdev, struct io_buffer *iobuf)
 Transmit packet.
static void icplus_poll_tx (struct net_device *netdev)
 Poll for completed packets.
static void icplus_poll_rx (struct net_device *netdev)
 Poll for received packets.
static void icplus_poll (struct net_device *netdev)
 Poll for completed and received packets.
static void icplus_irq (struct net_device *netdev, int enable)
 Enable or disable interrupts.
static int icplus_probe (struct pci_device *pci)
 Probe PCI device.
static void icplus_remove (struct pci_device *pci)
 Remove PCI device.

Variables

static const uint8_t icplus_mii_bits []
 Pin mapping for MII bit-bashing interface.
static struct bit_basher_operations icplus_basher_ops
 MII bit-bashing interface.
static struct net_device_operations icplus_operations
 IC+ network device operations.
static struct pci_device_id icplus_nics []
 IC+ PCI device IDs.
struct pci_driver icplus_driver __pci_driver
 IC+ PCI driver.

Detailed Description

IC+ network driver.

Definition in file icplus.c.


Function Documentation

FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL  )
static int icplus_reset ( struct icplus_nic icp) [static]

Reset hardware.

Parameters:
icpIC+ device
Return values:
rcReturn status code

Definition at line 58 of file icplus.c.

References DBGC, ETIMEDOUT, ICP_ASICCTRL, ICP_ASICCTRL_AUTOINIT, ICP_ASICCTRL_DMA, ICP_ASICCTRL_FIFO, ICP_ASICCTRL_GLOBALRESET, ICP_ASICCTRL_HOST, ICP_ASICCTRL_NETWORK, ICP_ASICCTRL_RESETBUSY, ICP_RESET_MAX_WAIT_MS, mdelay(), readl(), icplus_nic::regs, and writel().

Referenced by icplus_close(), icplus_open(), icplus_probe(), and icplus_remove().

                                                   {
        uint32_t asicctrl;
        unsigned int i;

        /* Trigger reset */
        writel ( ( ICP_ASICCTRL_GLOBALRESET | ICP_ASICCTRL_DMA |
                   ICP_ASICCTRL_FIFO | ICP_ASICCTRL_NETWORK | ICP_ASICCTRL_HOST |
                   ICP_ASICCTRL_AUTOINIT ), ( icp->regs + ICP_ASICCTRL ) );

        /* Wait for reset to complete */
        for ( i = 0 ; i < ICP_RESET_MAX_WAIT_MS ; i++ ) {

                /* Check if device is ready */
                asicctrl = readl ( icp->regs + ICP_ASICCTRL );
                if ( ! ( asicctrl & ICP_ASICCTRL_RESETBUSY ) )
                        return 0;

                /* Delay */
                mdelay ( 1 );
        }

        DBGC ( icp, "ICPLUS %p timed out waiting for reset (asicctrl %#08x)\n",
               icp, asicctrl );
        return -ETIMEDOUT;
}
static int icplus_read_eeprom ( struct nvs_device nvs,
unsigned int  address,
void *  data,
size_t  len 
) [static]

Read data from EEPROM.

Parameters:
nvsNVS device
addressAddress from which to read
dataData buffer
lenLength of data buffer
Return values:
rcReturn status code

Definition at line 100 of file icplus.c.

References assert, container_of, cpu_to_le16, data, DBGC, eeprom, ETIMEDOUT, ICP_EEPROM_MAX_WAIT_MS, ICP_EEPROMCTRL, ICP_EEPROMCTRL_ADDRESS, ICP_EEPROMCTRL_BUSY, ICP_EEPROMCTRL_OPCODE_READ, ICP_EEPROMDATA, mdelay(), readw(), icplus_nic::regs, and writew().

Referenced by icplus_init_eeprom().

                                                         {
        struct icplus_nic *icp =
                container_of ( nvs, struct icplus_nic, eeprom );
        unsigned int i;
        uint16_t eepromctrl;
        uint16_t *data_word = data;

        /* Sanity check.  We advertise a blocksize of one word, so
         * should only ever receive single-word requests.
         */
        assert ( len == sizeof ( *data_word ) );

        /* Initiate read */
        writew ( ( ICP_EEPROMCTRL_OPCODE_READ |
                   ICP_EEPROMCTRL_ADDRESS ( address ) ),
                 ( icp->regs + ICP_EEPROMCTRL ) );

        /* Wait for read to complete */
        for ( i = 0 ; i < ICP_EEPROM_MAX_WAIT_MS ; i++ ) {

                /* If read is not complete, delay 1ms and retry */
                eepromctrl = readw ( icp->regs + ICP_EEPROMCTRL );
                if ( eepromctrl & ICP_EEPROMCTRL_BUSY ) {
                        mdelay ( 1 );
                        continue;
                }

                /* Extract data */
                *data_word = cpu_to_le16 ( readw ( icp->regs + ICP_EEPROMDATA ));
                return 0;
        }

        DBGC ( icp, "ICPLUS %p timed out waiting for EEPROM read\n", icp );
        return -ETIMEDOUT;
}
static int icplus_write_eeprom ( struct nvs_device nvs,
unsigned int address  __unused,
const void *data  __unused,
size_t len  __unused 
) [static]

Write data to EEPROM.

Parameters:
nvsNVS device
addressAddress to which to write
dataData buffer
lenLength of data buffer
Return values:
rcReturn status code

Definition at line 146 of file icplus.c.

References container_of, DBGC, eeprom, and ENOTSUP.

Referenced by icplus_init_eeprom().

                                                       {
        struct icplus_nic *icp =
                container_of ( nvs, struct icplus_nic, eeprom );

        DBGC ( icp, "ICPLUS %p EEPROM write not supported\n", icp );
        return -ENOTSUP;
}
static void icplus_init_eeprom ( struct icplus_nic icp) [static]
static int icplus_mii_read_bit ( struct bit_basher basher,
unsigned int  bit_id 
) [static]

Read input bit.

Parameters:
basherBit-bashing interface
bit_idBit number
Return values:
zeroInput is a logic 0
non-zeroInput is a logic 1

Definition at line 194 of file icplus.c.

References mii_bit_basher::basher, container_of, DBG_DISABLE, DBG_ENABLE, DBGLVL_IO, ICP_PHYCTRL, icplus_mii_bits, icplus_nic::miibit, readb(), reg, and icplus_nic::regs.

                                                       {
        struct icplus_nic *icp = container_of ( basher, struct icplus_nic,
                                                miibit.basher );
        uint8_t mask = icplus_mii_bits[bit_id];
        uint8_t reg;

        DBG_DISABLE ( DBGLVL_IO );
        reg = readb ( icp->regs + ICP_PHYCTRL );
        DBG_ENABLE ( DBGLVL_IO );
        return ( reg & mask );
}
static void icplus_mii_write_bit ( struct bit_basher basher,
unsigned int  bit_id,
unsigned long  data 
) [static]

Set/clear output bit.

Parameters:
basherBit-bashing interface
bit_idBit number
dataValue to write

Definition at line 214 of file icplus.c.

References mii_bit_basher::basher, container_of, DBG_DISABLE, DBG_ENABLE, DBGLVL_IO, ICP_PHYCTRL, icplus_mii_bits, icplus_nic::miibit, readb(), reg, icplus_nic::regs, and writeb().

                                                                             {
        struct icplus_nic *icp = container_of ( basher, struct icplus_nic,
                                                miibit.basher );
        uint8_t mask = icplus_mii_bits[bit_id];
        uint8_t reg;

        DBG_DISABLE ( DBGLVL_IO );
        reg = readb ( icp->regs + ICP_PHYCTRL );
        reg &= ~mask;
        reg |= ( data & mask );
        writeb ( reg, icp->regs + ICP_PHYCTRL );
        readb ( icp->regs + ICP_PHYCTRL ); /* Ensure write reaches chip */
        DBG_ENABLE ( DBGLVL_IO );
}
static int icplus_init_phy ( struct icplus_nic icp) [static]

Configure PHY.

Parameters:
icpIC+ device
Return values:
rcReturn status code

Definition at line 249 of file icplus.c.

References ADVERTISE_1000FULL, DBGC, ICP_ASICCTRL, ICP_ASICCTRL_PHYSPEED1000, icplus_nic::mii, MII_CTRL1000, mii_find(), mii_reset(), mii_write(), rc, readl(), icplus_nic::regs, and strerror().

Referenced by icplus_probe().

                                                      {
        uint32_t asicctrl;
        int rc;

        /* Find PHY address */
        if ( ( rc = mii_find ( &icp->mii ) ) != 0 ) {
                DBGC ( icp, "ICPLUS %p could not find PHY address: %s\n",
                       icp, strerror ( rc ) );
                return rc;
        }

        /* Configure PHY to advertise 1000Mbps if applicable */
        asicctrl = readl ( icp->regs + ICP_ASICCTRL );
        if ( asicctrl & ICP_ASICCTRL_PHYSPEED1000 ) {
                if ( ( rc = mii_write ( &icp->mii, MII_CTRL1000,
                                        ADVERTISE_1000FULL ) ) != 0 ) {
                        DBGC ( icp, "ICPLUS %p could not advertise 1000Mbps: "
                               "%s\n", icp, strerror ( rc ) );
                        return rc;
                }
        }

        /* Reset PHY */
        if ( ( rc = mii_reset ( &icp->mii ) ) != 0 ) {
                DBGC ( icp, "ICPLUS %p could not reset PHY: %s\n",
                       icp, strerror ( rc ) );
                return rc;
        }

        return 0;
}
static void icplus_check_link ( struct net_device netdev) [static]

Check link state.

Parameters:
netdevNetwork device

Definition at line 286 of file icplus.c.

References DBGC, ICP_PHYCTRL, ICP_PHYCTRL_LINKSPEED, netdev_link_down(), netdev_link_up(), net_device::priv, readb(), and icplus_nic::regs.

Referenced by icplus_open(), icplus_poll(), and icplus_probe().

                                                            {
        struct icplus_nic *icp = netdev->priv;
        uint8_t phyctrl;

        /* Read link status */
        phyctrl = readb ( icp->regs + ICP_PHYCTRL );
        DBGC ( icp, "ICPLUS %p PHY control is %02x\n", icp, phyctrl );

        /* Update network device */
        if ( phyctrl & ICP_PHYCTRL_LINKSPEED ) {
                netdev_link_up ( netdev );
        } else {
                netdev_link_down ( netdev );
        }
}
static void icplus_set_base ( struct icplus_nic icp,
unsigned int  offset,
void *  base 
) [inline, static]

Set descriptor ring base address.

Parameters:
icpIC+ device
offsetRegister offset
addressBase address

Definition at line 316 of file icplus.c.

References ICP_BASE_HI, ICP_BASE_LO, phys, icplus_nic::regs, virt_to_bus(), and writel().

Referenced by icplus_open().

                                                  {
        physaddr_t phys = virt_to_bus ( base );

        /* Program base address registers */
        writel ( ( phys & 0xffffffffUL ),
                 ( icp->regs + offset + ICP_BASE_LO ) );
        if ( sizeof ( phys ) > sizeof ( uint32_t ) ) {
                writel ( ( ( ( uint64_t ) phys ) >> 32 ),
                         ( icp->regs + offset + ICP_BASE_HI ) );
        } else {
                writel ( 0, ( icp->regs + offset + ICP_BASE_HI ) );
        }
}
static int icplus_create_ring ( struct icplus_nic icp,
struct icplus_ring ring 
) [static]

Create descriptor ring.

Parameters:
icpIC+ device
ringDescriptor ring
Return values:
rcReturn status code

Definition at line 338 of file icplus.c.

References icplus_ring::cons, icplus_descriptor::control, cpu_to_le64, DBGC, ENOMEM, icplus_ring::entry, icplus_descriptor::flags, free_dma(), ICP_ALIGN, ICP_DONE, ICP_NUM_DESC, ICP_TFDLISTPTR, ICP_TX_INDICATE, ICP_TX_SOLE_FRAG, ICP_TX_UNALIGN, len, icplus_ring::listptr, malloc_dma(), memset(), icplus_descriptor::next, next, NULL, icplus_ring::prod, rc, and virt_to_bus().

Referenced by icplus_open().

                                                                                   {
        size_t len = ( sizeof ( ring->entry[0] ) * ICP_NUM_DESC );
        int rc;
        unsigned int i;
        struct icplus_descriptor *desc;
        struct icplus_descriptor *next;

        /* Allocate descriptor ring */
        ring->entry = malloc_dma ( len, ICP_ALIGN );
        if ( ! ring->entry ) {
                rc = -ENOMEM;
                goto err_alloc;
        }

        /* Initialise descriptor ring */
        memset ( ring->entry, 0, len );
        for ( i = 0 ; i < ICP_NUM_DESC ; i++ ) {
                desc = &ring->entry[i];
                next = &ring->entry[ ( i + 1 ) % ICP_NUM_DESC ];
                desc->next = cpu_to_le64 ( virt_to_bus ( next ) );
                desc->flags = ( ICP_TX_UNALIGN | ICP_TX_INDICATE );
                desc->control = ( ICP_TX_SOLE_FRAG | ICP_DONE );
        }

        /* Reset transmit producer & consumer counters */
        ring->prod = 0;
        ring->cons = 0;

        DBGC ( icp, "ICP %p %s ring at [%#08lx,%#08lx)\n",
               icp, ( ( ring->listptr == ICP_TFDLISTPTR ) ? "TX" : "RX" ),
               virt_to_bus ( ring->entry ),
               ( virt_to_bus ( ring->entry ) + len ) );
        return 0;

        free_dma ( ring->entry, len );
        ring->entry = NULL;
 err_alloc:
        return rc;
}
static void icplus_destroy_ring ( struct icplus_nic *icp  __unused,
struct icplus_ring ring 
) [static]

Destroy descriptor ring.

Parameters:
icpIC+ device
ringDescriptor ring

Definition at line 384 of file icplus.c.

References icplus_ring::entry, free_dma(), ICP_NUM_DESC, len, and NULL.

Referenced by icplus_close(), and icplus_open().

                                                             {
        size_t len = ( sizeof ( ring->entry[0] ) * ICP_NUM_DESC );

        /* Free descriptor ring */
        free_dma ( ring->entry, len );
        ring->entry = NULL;
}
void icplus_refill_rx ( struct icplus_nic icp)

Refill receive descriptor ring.

Parameters:
icpIC+ device

Definition at line 398 of file icplus.c.

References icplus_fragment::address, address, alloc_iob(), assert, icplus_ring::cons, icplus_descriptor::control, cpu_to_le16, cpu_to_le64, io_buffer::data, icplus_descriptor::data, DBGC2, icplus_ring::entry, ICP_DMACTRL, ICP_DMACTRL_RXPOLLNOW, ICP_NUM_DESC, ICP_RX_MAX_LEN, icplus_fragment::len, NULL, icplus_ring::prod, icplus_nic::regs, icplus_nic::rx, icplus_nic::rx_iobuf, virt_to_bus(), wmb, and writew().

Referenced by icplus_open(), and icplus_poll().

                                                 {
        struct icplus_descriptor *desc;
        struct io_buffer *iobuf;
        unsigned int rx_idx;
        physaddr_t address;
        unsigned int refilled = 0;

        /* Refill ring */
        while ( ( icp->rx.prod - icp->rx.cons ) < ICP_NUM_DESC ) {

                /* Allocate I/O buffer */
                iobuf = alloc_iob ( ICP_RX_MAX_LEN );
                if ( ! iobuf ) {
                        /* Wait for next refill */
                        break;
                }

                /* Get next receive descriptor */
                rx_idx = ( icp->rx.prod++ % ICP_NUM_DESC );
                desc = &icp->rx.entry[rx_idx];

                /* Populate receive descriptor */
                address = virt_to_bus ( iobuf->data );
                desc->data.address = cpu_to_le64 ( address );
                desc->data.len = cpu_to_le16 ( ICP_RX_MAX_LEN );
                wmb();
                desc->control = 0;

                /* Record I/O buffer */
                assert ( icp->rx_iobuf[rx_idx] == NULL );
                icp->rx_iobuf[rx_idx] = iobuf;

                DBGC2 ( icp, "ICP %p RX %d is [%llx,%llx)\n", icp, rx_idx,
                        ( ( unsigned long long ) address ),
                        ( ( unsigned long long ) address + ICP_RX_MAX_LEN ) );
                refilled++;
        }

        /* Push descriptors to card, if applicable */
        if ( refilled ) {
                wmb();
                writew ( ICP_DMACTRL_RXPOLLNOW, icp->regs + ICP_DMACTRL );
        }
}
static int icplus_open ( struct net_device netdev) [static]

Open network device.

Parameters:
netdevNetwork device
Return values:
rcReturn status code

Definition at line 449 of file icplus.c.

References icplus_ring::entry, ICP_MACCTRL, ICP_MACCTRL_DUPLEX, ICP_MACCTRL_RXENABLE, ICP_MACCTRL_TXENABLE, ICP_RXMODE, ICP_RXMODE_ALLFRAMES, ICP_RXMODE_BROADCAST, ICP_RXMODE_MULTICAST, ICP_RXMODE_UNICAST, icplus_check_link(), icplus_create_ring(), icplus_destroy_ring(), icplus_refill_rx(), icplus_reset(), icplus_set_base(), icplus_ring::listptr, net_device::priv, rc, icplus_nic::regs, icplus_nic::rx, icplus_nic::tx, writel(), and writew().

                                                     {
        struct icplus_nic *icp = netdev->priv;
        int rc;

        /* Create transmit descriptor ring */
        if ( ( rc = icplus_create_ring ( icp, &icp->tx ) ) != 0 )
                goto err_create_tx;

        /* Create receive descriptor ring */
        if ( ( rc = icplus_create_ring ( icp, &icp->rx ) ) != 0 )
                goto err_create_rx;

        /* Program descriptor base address */
        icplus_set_base ( icp, icp->tx.listptr, icp->tx.entry );
        icplus_set_base ( icp, icp->rx.listptr, icp->rx.entry );

        /* Enable receive mode */
        writew ( ( ICP_RXMODE_UNICAST | ICP_RXMODE_MULTICAST |
                   ICP_RXMODE_BROADCAST | ICP_RXMODE_ALLFRAMES ),
                 icp->regs + ICP_RXMODE );

        /* Enable transmitter and receiver */
        writel ( ( ICP_MACCTRL_TXENABLE | ICP_MACCTRL_RXENABLE |
                   ICP_MACCTRL_DUPLEX ), icp->regs + ICP_MACCTRL );

        /* Fill receive ring */
        icplus_refill_rx ( icp );

        /* Check link state */
        icplus_check_link ( netdev );

        return 0;

        icplus_reset ( icp );
        icplus_destroy_ring ( icp, &icp->rx );
 err_create_rx:
        icplus_destroy_ring ( icp, &icp->tx );
 err_create_tx:
        return rc;
}
static void icplus_close ( struct net_device netdev) [static]

Close network device.

Parameters:
netdevNetwork device

Definition at line 495 of file icplus.c.

References free_iob(), ICP_NUM_DESC, icplus_destroy_ring(), icplus_reset(), NULL, net_device::priv, icplus_nic::rx, icplus_nic::rx_iobuf, and icplus_nic::tx.

                                                       {
        struct icplus_nic *icp = netdev->priv;
        unsigned int i;

        /* Perform global reset */
        icplus_reset ( icp );

        /* Destroy receive descriptor ring */
        icplus_destroy_ring ( icp, &icp->rx );

        /* Destroy transmit descriptor ring */
        icplus_destroy_ring ( icp, &icp->tx );

        /* Discard any unused receive buffers */
        for ( i = 0 ; i < ICP_NUM_DESC ; i++ ) {
                if ( icp->rx_iobuf[i] )
                        free_iob ( icp->rx_iobuf[i] );
                icp->rx_iobuf[i] = NULL;
        }
}
static int icplus_transmit ( struct net_device netdev,
struct io_buffer iobuf 
) [static]

Transmit packet.

Parameters:
netdevNetwork device
iobufI/O buffer
Return values:
rcReturn status code

Definition at line 523 of file icplus.c.

References icplus_fragment::address, address, icplus_ring::cons, icplus_descriptor::control, cpu_to_le16, cpu_to_le64, io_buffer::data, icplus_descriptor::data, DBGC, DBGC2, DBGC2_HDA, ENOBUFS, icplus_ring::entry, ICP_DMACTRL, ICP_DMACTRL_TXPOLLNOW, ICP_NUM_DESC, ICP_TX_SOLE_FRAG, iob_len(), icplus_fragment::len, net_device::priv, icplus_ring::prod, icplus_nic::regs, icplus_nic::tx, virt_to_bus(), virt_to_phys(), wmb, and writew().

                                                       {
        struct icplus_nic *icp = netdev->priv;
        struct icplus_descriptor *desc;
        unsigned int tx_idx;
        physaddr_t address;

        /* Check if ring is full */
        if ( ( icp->tx.prod - icp->tx.cons ) >= ICP_NUM_DESC ) {
                DBGC ( icp, "ICP %p out of transmit descriptors\n", icp );
                return -ENOBUFS;
        }

        /* Find TX descriptor entry to use */
        tx_idx = ( icp->tx.prod++ % ICP_NUM_DESC );
        desc = &icp->tx.entry[tx_idx];

        /* Fill in TX descriptor */
        address = virt_to_bus ( iobuf->data );
        desc->data.address = cpu_to_le64 ( address );
        desc->data.len = cpu_to_le16 ( iob_len ( iobuf ) );
        wmb();
        desc->control = ICP_TX_SOLE_FRAG;
        wmb();

        /* Ring doorbell */
        writew ( ICP_DMACTRL_TXPOLLNOW, icp->regs + ICP_DMACTRL );

        DBGC2 ( icp, "ICP %p TX %d is [%llx,%llx)\n", icp, tx_idx,
                ( ( unsigned long long ) address ),
                ( ( unsigned long long ) address + iob_len ( iobuf ) ) );
        DBGC2_HDA ( icp, virt_to_phys ( desc ), desc, sizeof ( *desc ) );
        return 0;
}
static void icplus_poll_tx ( struct net_device netdev) [static]

Poll for completed packets.

Parameters:
netdevNetwork device

Definition at line 563 of file icplus.c.

References icplus_ring::cons, icplus_descriptor::control, DBGC2, icplus_ring::entry, ICP_DONE, ICP_NUM_DESC, netdev_tx_complete_next(), net_device::priv, icplus_ring::prod, and icplus_nic::tx.

Referenced by icplus_poll().

                                                         {
        struct icplus_nic *icp = netdev->priv;
        struct icplus_descriptor *desc;
        unsigned int tx_idx;

        /* Check for completed packets */
        while ( icp->tx.cons != icp->tx.prod ) {

                /* Get next transmit descriptor */
                tx_idx = ( icp->tx.cons % ICP_NUM_DESC );
                desc = &icp->tx.entry[tx_idx];

                /* Stop if descriptor is still in use */
                if ( ! ( desc->control & ICP_DONE ) )
                        return;

                /* Complete TX descriptor */
                DBGC2 ( icp, "ICP %p TX %d complete\n", icp, tx_idx );
                netdev_tx_complete_next ( netdev );
                icp->tx.cons++;
        }
}
static void icplus_poll_rx ( struct net_device netdev) [static]

Poll for received packets.

Parameters:
netdevNetwork device

Definition at line 591 of file icplus.c.

References icplus_ring::cons, icplus_descriptor::control, DBGC, DBGC2, EIO, icplus_ring::entry, icplus_descriptor::flags, ICP_DONE, ICP_NUM_DESC, ICP_RX_ERR_ALIGN, ICP_RX_ERR_FCS, ICP_RX_ERR_LEN, ICP_RX_ERR_OVERRUN, ICP_RX_ERR_OVERSIZED, ICP_RX_ERR_RUNT, iob_put, le16_to_cpu, icplus_descriptor::len, len, netdev_rx(), netdev_rx_err(), NULL, net_device::priv, icplus_ring::prod, icplus_nic::rx, and icplus_nic::rx_iobuf.

Referenced by icplus_poll().

                                                         {
        struct icplus_nic *icp = netdev->priv;
        struct icplus_descriptor *desc;
        struct io_buffer *iobuf;
        unsigned int rx_idx;
        size_t len;

        /* Check for received packets */
        while ( icp->rx.cons != icp->rx.prod ) {

                /* Get next transmit descriptor */
                rx_idx = ( icp->rx.cons % ICP_NUM_DESC );
                desc = &icp->rx.entry[rx_idx];

                /* Stop if descriptor is still in use */
                if ( ! ( desc->control & ICP_DONE ) )
                        return;

                /* Populate I/O buffer */
                iobuf = icp->rx_iobuf[rx_idx];
                icp->rx_iobuf[rx_idx] = NULL;
                len = le16_to_cpu ( desc->len );
                iob_put ( iobuf, len );

                /* Hand off to network stack */
                if ( desc->flags & ( ICP_RX_ERR_OVERRUN | ICP_RX_ERR_RUNT |
                                     ICP_RX_ERR_ALIGN | ICP_RX_ERR_FCS |
                                     ICP_RX_ERR_OVERSIZED | ICP_RX_ERR_LEN ) ) {
                        DBGC ( icp, "ICP %p RX %d error (length %zd, "
                               "flags %02x)\n", icp, rx_idx, len, desc->flags );
                        netdev_rx_err ( netdev, iobuf, -EIO );
                } else {
                        DBGC2 ( icp, "ICP %p RX %d complete (length "
                                "%zd)\n", icp, rx_idx, len );
                        netdev_rx ( netdev, iobuf );
                }
                icp->rx.cons++;
        }
}
static void icplus_poll ( struct net_device netdev) [static]

Poll for completed and received packets.

Parameters:
netdevNetwork device

Definition at line 636 of file icplus.c.

References DBGC, ICP_INTSTATUS, ICP_INTSTATUS_LINKEVENT, ICP_INTSTATUS_RXDMACOMPLETE, ICP_INTSTATUS_TXCOMPLETE, ICP_TXSTATUS, ICP_TXSTATUS_ERROR, icplus_check_link(), icplus_poll_rx(), icplus_poll_tx(), icplus_refill_rx(), net_device::priv, readl(), readw(), icplus_nic::regs, and writew().

                                                      {
        struct icplus_nic *icp = netdev->priv;
        uint16_t intstatus;
        uint32_t txstatus;

        /* Check for interrupts */
        intstatus = readw ( icp->regs + ICP_INTSTATUS );

        /* Poll for TX completions, if applicable */
        if ( intstatus & ICP_INTSTATUS_TXCOMPLETE ) {
                txstatus = readl ( icp->regs + ICP_TXSTATUS );
                if ( txstatus & ICP_TXSTATUS_ERROR )
                        DBGC ( icp, "ICP %p TX error: %08x\n", icp, txstatus );
                icplus_poll_tx ( netdev );
        }

        /* Poll for RX completions, if applicable */
        if ( intstatus & ICP_INTSTATUS_RXDMACOMPLETE ) {
                writew ( ICP_INTSTATUS_RXDMACOMPLETE, icp->regs + ICP_INTSTATUS );
                icplus_poll_rx ( netdev );
        }

        /* Check link state, if applicable */
        if ( intstatus & ICP_INTSTATUS_LINKEVENT ) {
                writew ( ICP_INTSTATUS_LINKEVENT, icp->regs + ICP_INTSTATUS );
                icplus_check_link ( netdev );
        }

        /* Refill receive ring */
        icplus_refill_rx ( icp );
}
static void icplus_irq ( struct net_device netdev,
int  enable 
) [static]

Enable or disable interrupts.

Parameters:
netdevNetwork device
enableInterrupts should be enabled

Definition at line 674 of file icplus.c.

References DBGC, and net_device::priv.

                                                                 {
        struct icplus_nic *icp = netdev->priv;

        DBGC ( icp, "ICPLUS %p does not yet support interrupts\n", icp );
        ( void ) enable;
}
static int icplus_probe ( struct pci_device pci) [static]

Probe PCI device.

Parameters:
pciPCI device
Return values:
rcReturn status code

Definition at line 703 of file icplus.c.

References adjust_pci_device(), alloc_etherdev(), mii_bit_basher::basher, DBGC, pci_device::dev, net_device::dev, icplus_nic::eeprom, ENODEV, ENOMEM, ETH_ALEN, net_device::hw_addr, ICP_BAR_SIZE, ICP_EEPROM_MAC, ICP_RFDLISTPTR, ICP_TFDLISTPTR, icplus_basher_ops, icplus_check_link(), icplus_init_eeprom(), icplus_init_phy(), icplus_reset(), init_mii_bit_basher(), ioremap(), iounmap(), icplus_ring::listptr, mii_bit_basher::mdio, pci_device::membase, memset(), icplus_nic::mii, mii_init(), icplus_nic::miibit, netdev, netdev_init(), netdev_nullify(), netdev_put(), nvs_read(), bit_basher::op, pci_set_drvdata(), net_device::priv, rc, register_netdev(), icplus_nic::regs, icplus_nic::rx, strerror(), icplus_nic::tx, and unregister_netdev().

                                                   {
        struct net_device *netdev;
        struct icplus_nic *icp;
        int rc;

        /* Allocate and initialise net device */
        netdev = alloc_etherdev ( sizeof ( *icp ) );
        if ( ! netdev ) {
                rc = -ENOMEM;
                goto err_alloc;
        }
        netdev_init ( netdev, &icplus_operations );
        icp = netdev->priv;
        pci_set_drvdata ( pci, netdev );
        netdev->dev = &pci->dev;
        memset ( icp, 0, sizeof ( *icp ) );
        icp->miibit.basher.op = &icplus_basher_ops;
        init_mii_bit_basher ( &icp->miibit );
        mii_init ( &icp->mii, &icp->miibit.mdio, 0 );
        icp->tx.listptr = ICP_TFDLISTPTR;
        icp->rx.listptr = ICP_RFDLISTPTR;

        /* Fix up PCI device */
        adjust_pci_device ( pci );

        /* Map registers */
        icp->regs = ioremap ( pci->membase, ICP_BAR_SIZE );
        if ( ! icp->regs ) {
                rc = -ENODEV;
                goto err_ioremap;
        }

        /* Reset the NIC */
        if ( ( rc = icplus_reset ( icp ) ) != 0 )
                goto err_reset;

        /* Initialise EEPROM */
        icplus_init_eeprom ( icp );

        /* Read EEPROM MAC address */
        if ( ( rc = nvs_read ( &icp->eeprom, ICP_EEPROM_MAC,
                               netdev->hw_addr, ETH_ALEN ) ) != 0 ) {
                DBGC ( icp, "ICPLUS %p could not read EEPROM MAC address: %s\n",
                       icp, strerror ( rc ) );
                goto err_eeprom;
        }

        /* Configure PHY */
        if ( ( rc = icplus_init_phy ( icp ) ) != 0 )
                goto err_phy;

        /* Register network device */
        if ( ( rc = register_netdev ( netdev ) ) != 0 )
                goto err_register_netdev;

        /* Set initial link state */
        icplus_check_link ( netdev );

        return 0;

        unregister_netdev ( netdev );
 err_register_netdev:
 err_phy:
 err_eeprom:
        icplus_reset ( icp );
 err_reset:
        iounmap ( icp->regs );
 err_ioremap:
        netdev_nullify ( netdev );
        netdev_put ( netdev );
 err_alloc:
        return rc;
}
static void icplus_remove ( struct pci_device pci) [static]

Remove PCI device.

Parameters:
pciPCI device

Definition at line 782 of file icplus.c.

References icplus_reset(), iounmap(), netdev, netdev_nullify(), netdev_put(), pci_get_drvdata(), net_device::priv, icplus_nic::regs, and unregister_netdev().

                                                     {
        struct net_device *netdev = pci_get_drvdata ( pci );
        struct icplus_nic *icp = netdev->priv;

        /* Unregister network device */
        unregister_netdev ( netdev );

        /* Reset card */
        icplus_reset ( icp );

        /* Free network device */
        iounmap ( icp->regs );
        netdev_nullify ( netdev );
        netdev_put ( netdev );
}

Variable Documentation

const uint8_t icplus_mii_bits[] [static]
Initial value:

Pin mapping for MII bit-bashing interface.

Definition at line 180 of file icplus.c.

Referenced by icplus_mii_read_bit(), and icplus_mii_write_bit().

Initial value:
 {
        .read = icplus_mii_read_bit,
        .write = icplus_mii_write_bit,
}

MII bit-bashing interface.

Definition at line 231 of file icplus.c.

Referenced by icplus_probe().

Initial value:
 {
        .open           = icplus_open,
        .close          = icplus_close,
        .transmit       = icplus_transmit,
        .poll           = icplus_poll,
        .irq            = icplus_irq,
}

IC+ network device operations.

Definition at line 682 of file icplus.c.

struct pci_device_id icplus_nics[] [static]
Initial value:
 {
        PCI_ROM ( 0x13f0, 0x1023, "ip1000a",    "IP1000A", 0 ),
}

IC+ PCI device IDs.

Definition at line 799 of file icplus.c.

struct pci_driver icplus_driver __pci_driver
Initial value:
 {
        .ids = icplus_nics,
        .id_count = ( sizeof ( icplus_nics ) / sizeof ( icplus_nics[0] ) ),
        .probe = icplus_probe,
        .remove = icplus_remove,
}

IC+ PCI driver.

Definition at line 804 of file icplus.c.