Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-2.6
* git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-2.6: (53 commits) tcp: Overflow bug in Vegas [IPv4] UFO: prevent generation of chained skb destined to UFO device iwlwifi: move the selects to the tristate drivers ipv4: annotate a few functions __init in ipconfig.c atm: ambassador: vcc_sf semaphore to mutex MAINTAINERS: The socketcan-core list is subscribers-only. netfilter: nf_conntrack: padding breaks conntrack hash on ARM ipv4: Update MTU to all related cache entries in ip_rt_frag_needed() sch_sfq: use del_timer_sync() in sfq_destroy() net: Add compat support for getsockopt (MCAST_MSFILTER) net: Several cleanups for the setsockopt compat support. ipvs: fix oops in backup for fwmark conn templates bridge: kernel panic when unloading bridge module bridge: fix error handling in br_add_if() netfilter: {nfnetlink,ip,ip6}_queue: fix skb_over_panic when enlarging packets netfilter: x_tables: fix net namespace leak when reading /proc/net/xxx_tables_names netfilter: xt_TCPOPTSTRIP: signed tcphoff for ipv6_skip_exthdr() retval tcp: Limit cwnd growth when deferring for GSO tcp: Allow send-limited cwnd to grow up to max_burst when gso disabled [netdrvr] gianfar: Determine TBIPA value dynamically ...
This commit is contained in:
commit
95dfec6ae1
@ -1039,7 +1039,7 @@ P: Urs Thuermann
|
||||
M: urs.thuermann@volkswagen.de
|
||||
P: Oliver Hartkopp
|
||||
M: oliver.hartkopp@volkswagen.de
|
||||
L: socketcan-core@lists.berlios.de
|
||||
L: socketcan-core@lists.berlios.de (subscribers-only)
|
||||
W: http://developer.berlios.de/projects/socketcan/
|
||||
S: Maintained
|
||||
|
||||
@ -3577,6 +3577,13 @@ M: pfg@sgi.com
|
||||
L: linux-ia64@vger.kernel.org
|
||||
S: Supported
|
||||
|
||||
SFC NETWORK DRIVER
|
||||
P: Steve Hodgson
|
||||
P: Ben Hutchings
|
||||
P: Robert Stonehouse
|
||||
M: linux-net-drivers@solarflare.com
|
||||
S: Supported
|
||||
|
||||
SGI VISUAL WORKSTATION 320 AND 540
|
||||
P: Andrey Panin
|
||||
M: pazke@donpac.ru
|
||||
|
@ -448,7 +448,9 @@ int npe_send_message(struct npe *npe, const void *msg, const char *what)
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
#if DEBUG_MSG > 1
|
||||
debug_msg(npe, "Sending a message took %i cycles\n", cycles);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -484,7 +486,9 @@ int npe_recv_message(struct npe *npe, void *msg, const char *what)
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
#if DEBUG_MSG > 1
|
||||
debug_msg(npe, "Receiving a message took %i cycles\n", cycles);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -184,6 +184,8 @@ void qmgr_release_queue(unsigned int queue)
|
||||
case 3: mask[0] = 0xFF; break;
|
||||
}
|
||||
|
||||
mask[1] = mask[2] = mask[3] = 0;
|
||||
|
||||
while (addr--)
|
||||
shift_mask(mask);
|
||||
|
||||
|
@ -33,6 +33,7 @@
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/poison.h>
|
||||
#include <linux/bitrev.h>
|
||||
#include <linux/mutex.h>
|
||||
|
||||
#include <asm/atomic.h>
|
||||
#include <asm/io.h>
|
||||
@ -1177,7 +1178,7 @@ static int amb_open (struct atm_vcc * atm_vcc)
|
||||
|
||||
vcc->tx_frame_bits = tx_frame_bits;
|
||||
|
||||
down (&dev->vcc_sf);
|
||||
mutex_lock(&dev->vcc_sf);
|
||||
if (dev->rxer[vci]) {
|
||||
// RXer on the channel already, just modify rate...
|
||||
cmd.request = cpu_to_be32 (SRB_MODIFY_VC_RATE);
|
||||
@ -1203,7 +1204,7 @@ static int amb_open (struct atm_vcc * atm_vcc)
|
||||
schedule();
|
||||
}
|
||||
dev->txer[vci].tx_present = 1;
|
||||
up (&dev->vcc_sf);
|
||||
mutex_unlock(&dev->vcc_sf);
|
||||
}
|
||||
|
||||
if (rxtp->traffic_class != ATM_NONE) {
|
||||
@ -1211,7 +1212,7 @@ static int amb_open (struct atm_vcc * atm_vcc)
|
||||
|
||||
vcc->rx_info.pool = pool;
|
||||
|
||||
down (&dev->vcc_sf);
|
||||
mutex_lock(&dev->vcc_sf);
|
||||
/* grow RX buffer pool */
|
||||
if (!dev->rxq[pool].buffers_wanted)
|
||||
dev->rxq[pool].buffers_wanted = rx_lats;
|
||||
@ -1237,7 +1238,7 @@ static int amb_open (struct atm_vcc * atm_vcc)
|
||||
schedule();
|
||||
// this link allows RX frames through
|
||||
dev->rxer[vci] = atm_vcc;
|
||||
up (&dev->vcc_sf);
|
||||
mutex_unlock(&dev->vcc_sf);
|
||||
}
|
||||
|
||||
// indicate readiness
|
||||
@ -1262,7 +1263,7 @@ static void amb_close (struct atm_vcc * atm_vcc) {
|
||||
if (atm_vcc->qos.txtp.traffic_class != ATM_NONE) {
|
||||
command cmd;
|
||||
|
||||
down (&dev->vcc_sf);
|
||||
mutex_lock(&dev->vcc_sf);
|
||||
if (dev->rxer[vci]) {
|
||||
// RXer still on the channel, just modify rate... XXX not really needed
|
||||
cmd.request = cpu_to_be32 (SRB_MODIFY_VC_RATE);
|
||||
@ -1277,7 +1278,7 @@ static void amb_close (struct atm_vcc * atm_vcc) {
|
||||
dev->txer[vci].tx_present = 0;
|
||||
while (command_do (dev, &cmd))
|
||||
schedule();
|
||||
up (&dev->vcc_sf);
|
||||
mutex_unlock(&dev->vcc_sf);
|
||||
}
|
||||
|
||||
// disable RXing
|
||||
@ -1287,7 +1288,7 @@ static void amb_close (struct atm_vcc * atm_vcc) {
|
||||
// this is (the?) one reason why we need the amb_vcc struct
|
||||
unsigned char pool = vcc->rx_info.pool;
|
||||
|
||||
down (&dev->vcc_sf);
|
||||
mutex_lock(&dev->vcc_sf);
|
||||
if (dev->txer[vci].tx_present) {
|
||||
// TXer still on the channel, just go to pool zero XXX not really needed
|
||||
cmd.request = cpu_to_be32 (SRB_MODIFY_VC_FLAGS);
|
||||
@ -1314,7 +1315,7 @@ static void amb_close (struct atm_vcc * atm_vcc) {
|
||||
dev->rxq[pool].buffers_wanted = 0;
|
||||
drain_rx_pool (dev, pool);
|
||||
}
|
||||
up (&dev->vcc_sf);
|
||||
mutex_unlock(&dev->vcc_sf);
|
||||
}
|
||||
|
||||
// free our structure
|
||||
@ -2188,7 +2189,7 @@ static void setup_dev(amb_dev *dev, struct pci_dev *pci_dev)
|
||||
|
||||
// semaphore for txer/rxer modifications - we cannot use a
|
||||
// spinlock as the critical region needs to switch processes
|
||||
init_MUTEX (&dev->vcc_sf);
|
||||
mutex_init(&dev->vcc_sf);
|
||||
// queue manipulation spinlocks; we want atomic reads and
|
||||
// writes to the queue descriptors (handles IRQ and SMP)
|
||||
// consider replacing "int pending" -> "atomic_t available"
|
||||
|
@ -638,7 +638,7 @@ struct amb_dev {
|
||||
amb_txq txq;
|
||||
amb_rxq rxq[NUM_RX_POOLS];
|
||||
|
||||
struct semaphore vcc_sf;
|
||||
struct mutex vcc_sf;
|
||||
amb_tx_info txer[NUM_VCS];
|
||||
struct atm_vcc * rxer[NUM_VCS];
|
||||
unsigned int tx_avail;
|
||||
|
@ -670,7 +670,7 @@ static irqreturn_t elp_interrupt(int irq, void *dev_id)
|
||||
memcpy(adapter->current_dma.target, adapter->dma_buffer, adapter->current_dma.length);
|
||||
}
|
||||
skb->protocol = eth_type_trans(skb,dev);
|
||||
adapter->stats.rx_bytes += skb->len;
|
||||
dev->stats.rx_bytes += skb->len;
|
||||
netif_rx(skb);
|
||||
dev->last_rx = jiffies;
|
||||
}
|
||||
@ -773,12 +773,12 @@ static irqreturn_t elp_interrupt(int irq, void *dev_id)
|
||||
* received board statistics
|
||||
*/
|
||||
case CMD_NETWORK_STATISTICS_RESPONSE:
|
||||
adapter->stats.rx_packets += adapter->irx_pcb.data.netstat.tot_recv;
|
||||
adapter->stats.tx_packets += adapter->irx_pcb.data.netstat.tot_xmit;
|
||||
adapter->stats.rx_crc_errors += adapter->irx_pcb.data.netstat.err_CRC;
|
||||
adapter->stats.rx_frame_errors += adapter->irx_pcb.data.netstat.err_align;
|
||||
adapter->stats.rx_fifo_errors += adapter->irx_pcb.data.netstat.err_ovrrun;
|
||||
adapter->stats.rx_over_errors += adapter->irx_pcb.data.netstat.err_res;
|
||||
dev->stats.rx_packets += adapter->irx_pcb.data.netstat.tot_recv;
|
||||
dev->stats.tx_packets += adapter->irx_pcb.data.netstat.tot_xmit;
|
||||
dev->stats.rx_crc_errors += adapter->irx_pcb.data.netstat.err_CRC;
|
||||
dev->stats.rx_frame_errors += adapter->irx_pcb.data.netstat.err_align;
|
||||
dev->stats.rx_fifo_errors += adapter->irx_pcb.data.netstat.err_ovrrun;
|
||||
dev->stats.rx_over_errors += adapter->irx_pcb.data.netstat.err_res;
|
||||
adapter->got[CMD_NETWORK_STATISTICS] = 1;
|
||||
if (elp_debug >= 3)
|
||||
printk(KERN_DEBUG "%s: interrupt - statistics response received\n", dev->name);
|
||||
@ -794,11 +794,11 @@ static irqreturn_t elp_interrupt(int irq, void *dev_id)
|
||||
break;
|
||||
switch (adapter->irx_pcb.data.xmit_resp.c_stat) {
|
||||
case 0xffff:
|
||||
adapter->stats.tx_aborted_errors++;
|
||||
dev->stats.tx_aborted_errors++;
|
||||
printk(KERN_INFO "%s: transmit timed out, network cable problem?\n", dev->name);
|
||||
break;
|
||||
case 0xfffe:
|
||||
adapter->stats.tx_fifo_errors++;
|
||||
dev->stats.tx_fifo_errors++;
|
||||
printk(KERN_INFO "%s: transmit timed out, FIFO underrun\n", dev->name);
|
||||
break;
|
||||
}
|
||||
@ -986,7 +986,7 @@ static bool send_packet(struct net_device *dev, struct sk_buff *skb)
|
||||
return false;
|
||||
}
|
||||
|
||||
adapter->stats.tx_bytes += nlen;
|
||||
dev->stats.tx_bytes += nlen;
|
||||
|
||||
/*
|
||||
* send the adapter a transmit packet command. Ignore segment and offset
|
||||
@ -1041,7 +1041,6 @@ static bool send_packet(struct net_device *dev, struct sk_buff *skb)
|
||||
|
||||
static void elp_timeout(struct net_device *dev)
|
||||
{
|
||||
elp_device *adapter = dev->priv;
|
||||
int stat;
|
||||
|
||||
stat = inb_status(dev->base_addr);
|
||||
@ -1049,7 +1048,7 @@ static void elp_timeout(struct net_device *dev)
|
||||
if (elp_debug >= 1)
|
||||
printk(KERN_DEBUG "%s: status %#02x\n", dev->name, stat);
|
||||
dev->trans_start = jiffies;
|
||||
adapter->stats.tx_dropped++;
|
||||
dev->stats.tx_dropped++;
|
||||
netif_wake_queue(dev);
|
||||
}
|
||||
|
||||
@ -1113,7 +1112,7 @@ static struct net_device_stats *elp_get_stats(struct net_device *dev)
|
||||
/* If the device is closed, just return the latest stats we have,
|
||||
- we cannot ask from the adapter without interrupts */
|
||||
if (!netif_running(dev))
|
||||
return &adapter->stats;
|
||||
return &dev->stats;
|
||||
|
||||
/* send a get statistics command to the board */
|
||||
adapter->tx_pcb.command = CMD_NETWORK_STATISTICS;
|
||||
@ -1126,12 +1125,12 @@ static struct net_device_stats *elp_get_stats(struct net_device *dev)
|
||||
while (adapter->got[CMD_NETWORK_STATISTICS] == 0 && time_before(jiffies, timeout));
|
||||
if (time_after_eq(jiffies, timeout)) {
|
||||
TIMEOUT_MSG(__LINE__);
|
||||
return &adapter->stats;
|
||||
return &dev->stats;
|
||||
}
|
||||
}
|
||||
|
||||
/* statistics are now up to date */
|
||||
return &adapter->stats;
|
||||
return &dev->stats;
|
||||
}
|
||||
|
||||
|
||||
@ -1571,7 +1570,6 @@ static int __init elplus_setup(struct net_device *dev)
|
||||
dev->set_multicast_list = elp_set_mc_list; /* local */
|
||||
dev->ethtool_ops = &netdev_ethtool_ops; /* local */
|
||||
|
||||
memset(&(adapter->stats), 0, sizeof(struct net_device_stats));
|
||||
dev->mem_start = dev->mem_end = 0;
|
||||
|
||||
err = register_netdev(dev);
|
||||
|
@ -264,7 +264,6 @@ typedef struct {
|
||||
pcb_struct rx_pcb; /* PCB for foreground receiving */
|
||||
pcb_struct itx_pcb; /* PCB for background sending */
|
||||
pcb_struct irx_pcb; /* PCB for background receiving */
|
||||
struct net_device_stats stats;
|
||||
|
||||
void *dma_buffer;
|
||||
|
||||
|
@ -167,7 +167,6 @@ enum RxFilter {
|
||||
enum el3_cardtype { EL3_ISA, EL3_PNP, EL3_MCA, EL3_EISA };
|
||||
|
||||
struct el3_private {
|
||||
struct net_device_stats stats;
|
||||
spinlock_t lock;
|
||||
/* skb send-queue */
|
||||
int head, size;
|
||||
@ -794,7 +793,6 @@ el3_open(struct net_device *dev)
|
||||
static void
|
||||
el3_tx_timeout (struct net_device *dev)
|
||||
{
|
||||
struct el3_private *lp = netdev_priv(dev);
|
||||
int ioaddr = dev->base_addr;
|
||||
|
||||
/* Transmitter timeout, serious problems. */
|
||||
@ -802,7 +800,7 @@ el3_tx_timeout (struct net_device *dev)
|
||||
"Tx FIFO room %d.\n",
|
||||
dev->name, inb(ioaddr + TX_STATUS), inw(ioaddr + EL3_STATUS),
|
||||
inw(ioaddr + TX_FREE));
|
||||
lp->stats.tx_errors++;
|
||||
dev->stats.tx_errors++;
|
||||
dev->trans_start = jiffies;
|
||||
/* Issue TX_RESET and TX_START commands. */
|
||||
outw(TxReset, ioaddr + EL3_CMD);
|
||||
@ -820,7 +818,7 @@ el3_start_xmit(struct sk_buff *skb, struct net_device *dev)
|
||||
|
||||
netif_stop_queue (dev);
|
||||
|
||||
lp->stats.tx_bytes += skb->len;
|
||||
dev->stats.tx_bytes += skb->len;
|
||||
|
||||
if (el3_debug > 4) {
|
||||
printk(KERN_DEBUG "%s: el3_start_xmit(length = %u) called, status %4.4x.\n",
|
||||
@ -881,7 +879,7 @@ el3_start_xmit(struct sk_buff *skb, struct net_device *dev)
|
||||
int i = 4;
|
||||
|
||||
while (--i > 0 && (tx_status = inb(ioaddr + TX_STATUS)) > 0) {
|
||||
if (tx_status & 0x38) lp->stats.tx_aborted_errors++;
|
||||
if (tx_status & 0x38) dev->stats.tx_aborted_errors++;
|
||||
if (tx_status & 0x30) outw(TxReset, ioaddr + EL3_CMD);
|
||||
if (tx_status & 0x3C) outw(TxEnable, ioaddr + EL3_CMD);
|
||||
outb(0x00, ioaddr + TX_STATUS); /* Pop the status stack. */
|
||||
@ -931,12 +929,11 @@ el3_interrupt(int irq, void *dev_id)
|
||||
outw(AckIntr | RxEarly, ioaddr + EL3_CMD);
|
||||
}
|
||||
if (status & TxComplete) { /* Really Tx error. */
|
||||
struct el3_private *lp = netdev_priv(dev);
|
||||
short tx_status;
|
||||
int i = 4;
|
||||
|
||||
while (--i>0 && (tx_status = inb(ioaddr + TX_STATUS)) > 0) {
|
||||
if (tx_status & 0x38) lp->stats.tx_aborted_errors++;
|
||||
if (tx_status & 0x38) dev->stats.tx_aborted_errors++;
|
||||
if (tx_status & 0x30) outw(TxReset, ioaddr + EL3_CMD);
|
||||
if (tx_status & 0x3C) outw(TxEnable, ioaddr + EL3_CMD);
|
||||
outb(0x00, ioaddr + TX_STATUS); /* Pop the status stack. */
|
||||
@ -1002,7 +999,7 @@ el3_get_stats(struct net_device *dev)
|
||||
spin_lock_irqsave(&lp->lock, flags);
|
||||
update_stats(dev);
|
||||
spin_unlock_irqrestore(&lp->lock, flags);
|
||||
return &lp->stats;
|
||||
return &dev->stats;
|
||||
}
|
||||
|
||||
/* Update statistics. We change to register window 6, so this should be run
|
||||
@ -1012,7 +1009,6 @@ el3_get_stats(struct net_device *dev)
|
||||
*/
|
||||
static void update_stats(struct net_device *dev)
|
||||
{
|
||||
struct el3_private *lp = netdev_priv(dev);
|
||||
int ioaddr = dev->base_addr;
|
||||
|
||||
if (el3_debug > 5)
|
||||
@ -1021,13 +1017,13 @@ static void update_stats(struct net_device *dev)
|
||||
outw(StatsDisable, ioaddr + EL3_CMD);
|
||||
/* Switch to the stats window, and read everything. */
|
||||
EL3WINDOW(6);
|
||||
lp->stats.tx_carrier_errors += inb(ioaddr + 0);
|
||||
lp->stats.tx_heartbeat_errors += inb(ioaddr + 1);
|
||||
dev->stats.tx_carrier_errors += inb(ioaddr + 0);
|
||||
dev->stats.tx_heartbeat_errors += inb(ioaddr + 1);
|
||||
/* Multiple collisions. */ inb(ioaddr + 2);
|
||||
lp->stats.collisions += inb(ioaddr + 3);
|
||||
lp->stats.tx_window_errors += inb(ioaddr + 4);
|
||||
lp->stats.rx_fifo_errors += inb(ioaddr + 5);
|
||||
lp->stats.tx_packets += inb(ioaddr + 6);
|
||||
dev->stats.collisions += inb(ioaddr + 3);
|
||||
dev->stats.tx_window_errors += inb(ioaddr + 4);
|
||||
dev->stats.rx_fifo_errors += inb(ioaddr + 5);
|
||||
dev->stats.tx_packets += inb(ioaddr + 6);
|
||||
/* Rx packets */ inb(ioaddr + 7);
|
||||
/* Tx deferrals */ inb(ioaddr + 8);
|
||||
inw(ioaddr + 10); /* Total Rx and Tx octets. */
|
||||
@ -1042,7 +1038,6 @@ static void update_stats(struct net_device *dev)
|
||||
static int
|
||||
el3_rx(struct net_device *dev)
|
||||
{
|
||||
struct el3_private *lp = netdev_priv(dev);
|
||||
int ioaddr = dev->base_addr;
|
||||
short rx_status;
|
||||
|
||||
@ -1054,21 +1049,21 @@ el3_rx(struct net_device *dev)
|
||||
short error = rx_status & 0x3800;
|
||||
|
||||
outw(RxDiscard, ioaddr + EL3_CMD);
|
||||
lp->stats.rx_errors++;
|
||||
dev->stats.rx_errors++;
|
||||
switch (error) {
|
||||
case 0x0000: lp->stats.rx_over_errors++; break;
|
||||
case 0x0800: lp->stats.rx_length_errors++; break;
|
||||
case 0x1000: lp->stats.rx_frame_errors++; break;
|
||||
case 0x1800: lp->stats.rx_length_errors++; break;
|
||||
case 0x2000: lp->stats.rx_frame_errors++; break;
|
||||
case 0x2800: lp->stats.rx_crc_errors++; break;
|
||||
case 0x0000: dev->stats.rx_over_errors++; break;
|
||||
case 0x0800: dev->stats.rx_length_errors++; break;
|
||||
case 0x1000: dev->stats.rx_frame_errors++; break;
|
||||
case 0x1800: dev->stats.rx_length_errors++; break;
|
||||
case 0x2000: dev->stats.rx_frame_errors++; break;
|
||||
case 0x2800: dev->stats.rx_crc_errors++; break;
|
||||
}
|
||||
} else {
|
||||
short pkt_len = rx_status & 0x7ff;
|
||||
struct sk_buff *skb;
|
||||
|
||||
skb = dev_alloc_skb(pkt_len+5);
|
||||
lp->stats.rx_bytes += pkt_len;
|
||||
dev->stats.rx_bytes += pkt_len;
|
||||
if (el3_debug > 4)
|
||||
printk("Receiving packet size %d status %4.4x.\n",
|
||||
pkt_len, rx_status);
|
||||
@ -1083,11 +1078,11 @@ el3_rx(struct net_device *dev)
|
||||
skb->protocol = eth_type_trans(skb,dev);
|
||||
netif_rx(skb);
|
||||
dev->last_rx = jiffies;
|
||||
lp->stats.rx_packets++;
|
||||
dev->stats.rx_packets++;
|
||||
continue;
|
||||
}
|
||||
outw(RxDiscard, ioaddr + EL3_CMD);
|
||||
lp->stats.rx_dropped++;
|
||||
dev->stats.rx_dropped++;
|
||||
if (el3_debug)
|
||||
printk("%s: Couldn't allocate a sk_buff of size %d.\n",
|
||||
dev->name, pkt_len);
|
||||
|
@ -310,7 +310,6 @@ struct corkscrew_private {
|
||||
struct sk_buff *tx_skbuff[TX_RING_SIZE];
|
||||
unsigned int cur_rx, cur_tx; /* The next free ring entry */
|
||||
unsigned int dirty_rx, dirty_tx;/* The ring entries to be free()ed. */
|
||||
struct net_device_stats stats;
|
||||
struct sk_buff *tx_skb; /* Packet being eaten by bus master ctrl. */
|
||||
struct timer_list timer; /* Media selection timer. */
|
||||
int capabilities ; /* Adapter capabilities word. */
|
||||
@ -983,8 +982,8 @@ static void corkscrew_timeout(struct net_device *dev)
|
||||
break;
|
||||
outw(TxEnable, ioaddr + EL3_CMD);
|
||||
dev->trans_start = jiffies;
|
||||
vp->stats.tx_errors++;
|
||||
vp->stats.tx_dropped++;
|
||||
dev->stats.tx_errors++;
|
||||
dev->stats.tx_dropped++;
|
||||
netif_wake_queue(dev);
|
||||
}
|
||||
|
||||
@ -1050,7 +1049,7 @@ static int corkscrew_start_xmit(struct sk_buff *skb,
|
||||
}
|
||||
/* Put out the doubleword header... */
|
||||
outl(skb->len, ioaddr + TX_FIFO);
|
||||
vp->stats.tx_bytes += skb->len;
|
||||
dev->stats.tx_bytes += skb->len;
|
||||
#ifdef VORTEX_BUS_MASTER
|
||||
if (vp->bus_master) {
|
||||
/* Set the bus-master controller to transfer the packet. */
|
||||
@ -1094,9 +1093,9 @@ static int corkscrew_start_xmit(struct sk_buff *skb,
|
||||
printk("%s: Tx error, status %2.2x.\n",
|
||||
dev->name, tx_status);
|
||||
if (tx_status & 0x04)
|
||||
vp->stats.tx_fifo_errors++;
|
||||
dev->stats.tx_fifo_errors++;
|
||||
if (tx_status & 0x38)
|
||||
vp->stats.tx_aborted_errors++;
|
||||
dev->stats.tx_aborted_errors++;
|
||||
if (tx_status & 0x30) {
|
||||
int j;
|
||||
outw(TxReset, ioaddr + EL3_CMD);
|
||||
@ -1257,7 +1256,6 @@ static irqreturn_t corkscrew_interrupt(int irq, void *dev_id)
|
||||
|
||||
static int corkscrew_rx(struct net_device *dev)
|
||||
{
|
||||
struct corkscrew_private *vp = netdev_priv(dev);
|
||||
int ioaddr = dev->base_addr;
|
||||
int i;
|
||||
short rx_status;
|
||||
@ -1271,17 +1269,17 @@ static int corkscrew_rx(struct net_device *dev)
|
||||
if (corkscrew_debug > 2)
|
||||
printk(" Rx error: status %2.2x.\n",
|
||||
rx_error);
|
||||
vp->stats.rx_errors++;
|
||||
dev->stats.rx_errors++;
|
||||
if (rx_error & 0x01)
|
||||
vp->stats.rx_over_errors++;
|
||||
dev->stats.rx_over_errors++;
|
||||
if (rx_error & 0x02)
|
||||
vp->stats.rx_length_errors++;
|
||||
dev->stats.rx_length_errors++;
|
||||
if (rx_error & 0x04)
|
||||
vp->stats.rx_frame_errors++;
|
||||
dev->stats.rx_frame_errors++;
|
||||
if (rx_error & 0x08)
|
||||
vp->stats.rx_crc_errors++;
|
||||
dev->stats.rx_crc_errors++;
|
||||
if (rx_error & 0x10)
|
||||
vp->stats.rx_length_errors++;
|
||||
dev->stats.rx_length_errors++;
|
||||
} else {
|
||||
/* The packet length: up to 4.5K!. */
|
||||
short pkt_len = rx_status & 0x1fff;
|
||||
@ -1301,8 +1299,8 @@ static int corkscrew_rx(struct net_device *dev)
|
||||
skb->protocol = eth_type_trans(skb, dev);
|
||||
netif_rx(skb);
|
||||
dev->last_rx = jiffies;
|
||||
vp->stats.rx_packets++;
|
||||
vp->stats.rx_bytes += pkt_len;
|
||||
dev->stats.rx_packets++;
|
||||
dev->stats.rx_bytes += pkt_len;
|
||||
/* Wait a limited time to go to next packet. */
|
||||
for (i = 200; i >= 0; i--)
|
||||
if (! (inw(ioaddr + EL3_STATUS) & CmdInProgress))
|
||||
@ -1312,7 +1310,7 @@ static int corkscrew_rx(struct net_device *dev)
|
||||
printk("%s: Couldn't allocate a sk_buff of size %d.\n", dev->name, pkt_len);
|
||||
}
|
||||
outw(RxDiscard, ioaddr + EL3_CMD);
|
||||
vp->stats.rx_dropped++;
|
||||
dev->stats.rx_dropped++;
|
||||
/* Wait a limited time to skip this packet. */
|
||||
for (i = 200; i >= 0; i--)
|
||||
if (!(inw(ioaddr + EL3_STATUS) & CmdInProgress))
|
||||
@ -1337,23 +1335,23 @@ static int boomerang_rx(struct net_device *dev)
|
||||
if (corkscrew_debug > 2)
|
||||
printk(" Rx error: status %2.2x.\n",
|
||||
rx_error);
|
||||
vp->stats.rx_errors++;
|
||||
dev->stats.rx_errors++;
|
||||
if (rx_error & 0x01)
|
||||
vp->stats.rx_over_errors++;
|
||||
dev->stats.rx_over_errors++;
|
||||
if (rx_error & 0x02)
|
||||
vp->stats.rx_length_errors++;
|
||||
dev->stats.rx_length_errors++;
|
||||
if (rx_error & 0x04)
|
||||
vp->stats.rx_frame_errors++;
|
||||
dev->stats.rx_frame_errors++;
|
||||
if (rx_error & 0x08)
|
||||
vp->stats.rx_crc_errors++;
|
||||
dev->stats.rx_crc_errors++;
|
||||
if (rx_error & 0x10)
|
||||
vp->stats.rx_length_errors++;
|
||||
dev->stats.rx_length_errors++;
|
||||
} else {
|
||||
/* The packet length: up to 4.5K!. */
|
||||
short pkt_len = rx_status & 0x1fff;
|
||||
struct sk_buff *skb;
|
||||
|
||||
vp->stats.rx_bytes += pkt_len;
|
||||
dev->stats.rx_bytes += pkt_len;
|
||||
if (corkscrew_debug > 4)
|
||||
printk("Receiving packet size %d status %4.4x.\n",
|
||||
pkt_len, rx_status);
|
||||
@ -1388,7 +1386,7 @@ static int boomerang_rx(struct net_device *dev)
|
||||
skb->protocol = eth_type_trans(skb, dev);
|
||||
netif_rx(skb);
|
||||
dev->last_rx = jiffies;
|
||||
vp->stats.rx_packets++;
|
||||
dev->stats.rx_packets++;
|
||||
}
|
||||
entry = (++vp->cur_rx) % RX_RING_SIZE;
|
||||
}
|
||||
@ -1475,7 +1473,7 @@ static struct net_device_stats *corkscrew_get_stats(struct net_device *dev)
|
||||
update_stats(dev->base_addr, dev);
|
||||
spin_unlock_irqrestore(&vp->lock, flags);
|
||||
}
|
||||
return &vp->stats;
|
||||
return &dev->stats;
|
||||
}
|
||||
|
||||
/* Update statistics.
|
||||
@ -1487,19 +1485,17 @@ static struct net_device_stats *corkscrew_get_stats(struct net_device *dev)
|
||||
*/
|
||||
static void update_stats(int ioaddr, struct net_device *dev)
|
||||
{
|
||||
struct corkscrew_private *vp = netdev_priv(dev);
|
||||
|
||||
/* Unlike the 3c5x9 we need not turn off stats updates while reading. */
|
||||
/* Switch to the stats window, and read everything. */
|
||||
EL3WINDOW(6);
|
||||
vp->stats.tx_carrier_errors += inb(ioaddr + 0);
|
||||
vp->stats.tx_heartbeat_errors += inb(ioaddr + 1);
|
||||
dev->stats.tx_carrier_errors += inb(ioaddr + 0);
|
||||
dev->stats.tx_heartbeat_errors += inb(ioaddr + 1);
|
||||
/* Multiple collisions. */ inb(ioaddr + 2);
|
||||
vp->stats.collisions += inb(ioaddr + 3);
|
||||
vp->stats.tx_window_errors += inb(ioaddr + 4);
|
||||
vp->stats.rx_fifo_errors += inb(ioaddr + 5);
|
||||
vp->stats.tx_packets += inb(ioaddr + 6);
|
||||
vp->stats.tx_packets += (inb(ioaddr + 9) & 0x30) << 4;
|
||||
dev->stats.collisions += inb(ioaddr + 3);
|
||||
dev->stats.tx_window_errors += inb(ioaddr + 4);
|
||||
dev->stats.rx_fifo_errors += inb(ioaddr + 5);
|
||||
dev->stats.tx_packets += inb(ioaddr + 6);
|
||||
dev->stats.tx_packets += (inb(ioaddr + 9) & 0x30) << 4;
|
||||
/* Rx packets */ inb(ioaddr + 7);
|
||||
/* Must read to clear */
|
||||
/* Tx deferrals */ inb(ioaddr + 8);
|
||||
|
@ -2593,6 +2593,7 @@ config BNX2X
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called bnx2x. This is recommended.
|
||||
|
||||
source "drivers/net/sfc/Kconfig"
|
||||
|
||||
endif # NETDEV_10000
|
||||
|
||||
|
@ -253,3 +253,5 @@ obj-$(CONFIG_FS_ENET) += fs_enet/
|
||||
obj-$(CONFIG_NETXEN_NIC) += netxen/
|
||||
obj-$(CONFIG_NIU) += niu.o
|
||||
obj-$(CONFIG_VIRTIO_NET) += virtio_net.o
|
||||
obj-$(CONFIG_SFC) += sfc/
|
||||
|
||||
|
@ -47,3 +47,11 @@ config EP93XX_ETH
|
||||
help
|
||||
This is a driver for the ethernet hardware included in EP93xx CPUs.
|
||||
Say Y if you are building a kernel for EP93xx based devices.
|
||||
|
||||
config IXP4XX_ETH
|
||||
tristate "Intel IXP4xx Ethernet support"
|
||||
depends on ARM && ARCH_IXP4XX && IXP4XX_NPE && IXP4XX_QMGR
|
||||
select MII
|
||||
help
|
||||
Say Y here if you want to use built-in Ethernet ports
|
||||
on IXP4xx processor.
|
||||
|
@ -9,3 +9,4 @@ obj-$(CONFIG_ARM_ETHER3) += ether3.o
|
||||
obj-$(CONFIG_ARM_ETHER1) += ether1.o
|
||||
obj-$(CONFIG_ARM_AT91_ETHER) += at91_ether.o
|
||||
obj-$(CONFIG_EP93XX_ETH) += ep93xx_eth.o
|
||||
obj-$(CONFIG_IXP4XX_ETH) += ixp4xx_eth.o
|
||||
|
1265
drivers/net/arm/ixp4xx_eth.c
Normal file
1265
drivers/net/arm/ixp4xx_eth.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -27,6 +27,7 @@
|
||||
#include <linux/phy.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/ethtool.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
@ -42,7 +43,7 @@
|
||||
#define DRV_NAME "bfin_mac"
|
||||
#define DRV_VERSION "1.1"
|
||||
#define DRV_AUTHOR "Bryan Wu, Luke Yang"
|
||||
#define DRV_DESC "Blackfin BF53[67] BF527 on-chip Ethernet MAC driver"
|
||||
#define DRV_DESC "Blackfin on-chip Ethernet MAC driver"
|
||||
|
||||
MODULE_AUTHOR(DRV_AUTHOR);
|
||||
MODULE_LICENSE("GPL");
|
||||
@ -73,8 +74,14 @@ static struct net_dma_desc_tx *current_tx_ptr;
|
||||
static struct net_dma_desc_tx *tx_desc;
|
||||
static struct net_dma_desc_rx *rx_desc;
|
||||
|
||||
static void bf537mac_disable(void);
|
||||
static void bf537mac_enable(void);
|
||||
#if defined(CONFIG_BFIN_MAC_RMII)
|
||||
static u16 pin_req[] = P_RMII0;
|
||||
#else
|
||||
static u16 pin_req[] = P_MII0;
|
||||
#endif
|
||||
|
||||
static void bfin_mac_disable(void);
|
||||
static void bfin_mac_enable(void);
|
||||
|
||||
static void desc_list_free(void)
|
||||
{
|
||||
@ -243,27 +250,6 @@ init_error:
|
||||
|
||||
/*---PHY CONTROL AND CONFIGURATION-----------------------------------------*/
|
||||
|
||||
/* Set FER regs to MUX in Ethernet pins */
|
||||
static int setup_pin_mux(int action)
|
||||
{
|
||||
#if defined(CONFIG_BFIN_MAC_RMII)
|
||||
u16 pin_req[] = P_RMII0;
|
||||
#else
|
||||
u16 pin_req[] = P_MII0;
|
||||
#endif
|
||||
|
||||
if (action) {
|
||||
if (peripheral_request_list(pin_req, DRV_NAME)) {
|
||||
printk(KERN_ERR DRV_NAME
|
||||
": Requesting Peripherals failed\n");
|
||||
return -EFAULT;
|
||||
}
|
||||
} else
|
||||
peripheral_free_list(pin_req);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* MII operations
|
||||
*/
|
||||
@ -322,9 +308,9 @@ static int mdiobus_reset(struct mii_bus *bus)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void bf537_adjust_link(struct net_device *dev)
|
||||
static void bfin_mac_adjust_link(struct net_device *dev)
|
||||
{
|
||||
struct bf537mac_local *lp = netdev_priv(dev);
|
||||
struct bfin_mac_local *lp = netdev_priv(dev);
|
||||
struct phy_device *phydev = lp->phydev;
|
||||
unsigned long flags;
|
||||
int new_state = 0;
|
||||
@ -395,7 +381,7 @@ static void bf537_adjust_link(struct net_device *dev)
|
||||
|
||||
static int mii_probe(struct net_device *dev)
|
||||
{
|
||||
struct bf537mac_local *lp = netdev_priv(dev);
|
||||
struct bfin_mac_local *lp = netdev_priv(dev);
|
||||
struct phy_device *phydev = NULL;
|
||||
unsigned short sysctl;
|
||||
int i;
|
||||
@ -431,10 +417,10 @@ static int mii_probe(struct net_device *dev)
|
||||
}
|
||||
|
||||
#if defined(CONFIG_BFIN_MAC_RMII)
|
||||
phydev = phy_connect(dev, phydev->dev.bus_id, &bf537_adjust_link, 0,
|
||||
phydev = phy_connect(dev, phydev->dev.bus_id, &bfin_mac_adjust_link, 0,
|
||||
PHY_INTERFACE_MODE_RMII);
|
||||
#else
|
||||
phydev = phy_connect(dev, phydev->dev.bus_id, &bf537_adjust_link, 0,
|
||||
phydev = phy_connect(dev, phydev->dev.bus_id, &bfin_mac_adjust_link, 0,
|
||||
PHY_INTERFACE_MODE_MII);
|
||||
#endif
|
||||
|
||||
@ -469,6 +455,51 @@ static int mii_probe(struct net_device *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Ethtool support
|
||||
*/
|
||||
|
||||
static int
|
||||
bfin_mac_ethtool_getsettings(struct net_device *dev, struct ethtool_cmd *cmd)
|
||||
{
|
||||
struct bfin_mac_local *lp = netdev_priv(dev);
|
||||
|
||||
if (lp->phydev)
|
||||
return phy_ethtool_gset(lp->phydev, cmd);
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int
|
||||
bfin_mac_ethtool_setsettings(struct net_device *dev, struct ethtool_cmd *cmd)
|
||||
{
|
||||
struct bfin_mac_local *lp = netdev_priv(dev);
|
||||
|
||||
if (!capable(CAP_NET_ADMIN))
|
||||
return -EPERM;
|
||||
|
||||
if (lp->phydev)
|
||||
return phy_ethtool_sset(lp->phydev, cmd);
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static void bfin_mac_ethtool_getdrvinfo(struct net_device *dev,
|
||||
struct ethtool_drvinfo *info)
|
||||
{
|
||||
strcpy(info->driver, DRV_NAME);
|
||||
strcpy(info->version, DRV_VERSION);
|
||||
strcpy(info->fw_version, "N/A");
|
||||
strcpy(info->bus_info, dev->dev.bus_id);
|
||||
}
|
||||
|
||||
static struct ethtool_ops bfin_mac_ethtool_ops = {
|
||||
.get_settings = bfin_mac_ethtool_getsettings,
|
||||
.set_settings = bfin_mac_ethtool_setsettings,
|
||||
.get_link = ethtool_op_get_link,
|
||||
.get_drvinfo = bfin_mac_ethtool_getdrvinfo,
|
||||
};
|
||||
|
||||
/**************************************************************************/
|
||||
void setup_system_regs(struct net_device *dev)
|
||||
{
|
||||
@ -511,7 +542,7 @@ static void setup_mac_addr(u8 *mac_addr)
|
||||
bfin_write_EMAC_ADDRHI(addr_hi);
|
||||
}
|
||||
|
||||
static int bf537mac_set_mac_address(struct net_device *dev, void *p)
|
||||
static int bfin_mac_set_mac_address(struct net_device *dev, void *p)
|
||||
{
|
||||
struct sockaddr *addr = p;
|
||||
if (netif_running(dev))
|
||||
@ -573,7 +604,7 @@ adjust_head:
|
||||
|
||||
}
|
||||
|
||||
static int bf537mac_hard_start_xmit(struct sk_buff *skb,
|
||||
static int bfin_mac_hard_start_xmit(struct sk_buff *skb,
|
||||
struct net_device *dev)
|
||||
{
|
||||
unsigned int data;
|
||||
@ -631,7 +662,7 @@ out:
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void bf537mac_rx(struct net_device *dev)
|
||||
static void bfin_mac_rx(struct net_device *dev)
|
||||
{
|
||||
struct sk_buff *skb, *new_skb;
|
||||
unsigned short len;
|
||||
@ -680,7 +711,7 @@ out:
|
||||
}
|
||||
|
||||
/* interrupt routine to handle rx and error signal */
|
||||
static irqreturn_t bf537mac_interrupt(int irq, void *dev_id)
|
||||
static irqreturn_t bfin_mac_interrupt(int irq, void *dev_id)
|
||||
{
|
||||
struct net_device *dev = dev_id;
|
||||
int number = 0;
|
||||
@ -700,21 +731,21 @@ get_one_packet:
|
||||
}
|
||||
|
||||
real_rx:
|
||||
bf537mac_rx(dev);
|
||||
bfin_mac_rx(dev);
|
||||
number++;
|
||||
goto get_one_packet;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_NET_POLL_CONTROLLER
|
||||
static void bf537mac_poll(struct net_device *dev)
|
||||
static void bfin_mac_poll(struct net_device *dev)
|
||||
{
|
||||
disable_irq(IRQ_MAC_RX);
|
||||
bf537mac_interrupt(IRQ_MAC_RX, dev);
|
||||
bfin_mac_interrupt(IRQ_MAC_RX, dev);
|
||||
enable_irq(IRQ_MAC_RX);
|
||||
}
|
||||
#endif /* CONFIG_NET_POLL_CONTROLLER */
|
||||
|
||||
static void bf537mac_disable(void)
|
||||
static void bfin_mac_disable(void)
|
||||
{
|
||||
unsigned int opmode;
|
||||
|
||||
@ -728,7 +759,7 @@ static void bf537mac_disable(void)
|
||||
/*
|
||||
* Enable Interrupts, Receive, and Transmit
|
||||
*/
|
||||
static void bf537mac_enable(void)
|
||||
static void bfin_mac_enable(void)
|
||||
{
|
||||
u32 opmode;
|
||||
|
||||
@ -766,23 +797,23 @@ static void bf537mac_enable(void)
|
||||
}
|
||||
|
||||
/* Our watchdog timed out. Called by the networking layer */
|
||||
static void bf537mac_timeout(struct net_device *dev)
|
||||
static void bfin_mac_timeout(struct net_device *dev)
|
||||
{
|
||||
pr_debug("%s: %s\n", dev->name, __FUNCTION__);
|
||||
|
||||
bf537mac_disable();
|
||||
bfin_mac_disable();
|
||||
|
||||
/* reset tx queue */
|
||||
tx_list_tail = tx_list_head->next;
|
||||
|
||||
bf537mac_enable();
|
||||
bfin_mac_enable();
|
||||
|
||||
/* We can accept TX packets again */
|
||||
dev->trans_start = jiffies;
|
||||
netif_wake_queue(dev);
|
||||
}
|
||||
|
||||
static void bf537mac_multicast_hash(struct net_device *dev)
|
||||
static void bfin_mac_multicast_hash(struct net_device *dev)
|
||||
{
|
||||
u32 emac_hashhi, emac_hashlo;
|
||||
struct dev_mc_list *dmi = dev->mc_list;
|
||||
@ -821,7 +852,7 @@ static void bf537mac_multicast_hash(struct net_device *dev)
|
||||
* promiscuous mode (for TCPDUMP and cousins) or accept
|
||||
* a select set of multicast packets
|
||||
*/
|
||||
static void bf537mac_set_multicast_list(struct net_device *dev)
|
||||
static void bfin_mac_set_multicast_list(struct net_device *dev)
|
||||
{
|
||||
u32 sysctl;
|
||||
|
||||
@ -840,7 +871,7 @@ static void bf537mac_set_multicast_list(struct net_device *dev)
|
||||
sysctl = bfin_read_EMAC_OPMODE();
|
||||
sysctl |= HM;
|
||||
bfin_write_EMAC_OPMODE(sysctl);
|
||||
bf537mac_multicast_hash(dev);
|
||||
bfin_mac_multicast_hash(dev);
|
||||
} else {
|
||||
/* clear promisc or multicast mode */
|
||||
sysctl = bfin_read_EMAC_OPMODE();
|
||||
@ -852,7 +883,7 @@ static void bf537mac_set_multicast_list(struct net_device *dev)
|
||||
/*
|
||||
* this puts the device in an inactive state
|
||||
*/
|
||||
static void bf537mac_shutdown(struct net_device *dev)
|
||||
static void bfin_mac_shutdown(struct net_device *dev)
|
||||
{
|
||||
/* Turn off the EMAC */
|
||||
bfin_write_EMAC_OPMODE(0x00000000);
|
||||
@ -866,9 +897,9 @@ static void bf537mac_shutdown(struct net_device *dev)
|
||||
*
|
||||
* Set up everything, reset the card, etc..
|
||||
*/
|
||||
static int bf537mac_open(struct net_device *dev)
|
||||
static int bfin_mac_open(struct net_device *dev)
|
||||
{
|
||||
struct bf537mac_local *lp = netdev_priv(dev);
|
||||
struct bfin_mac_local *lp = netdev_priv(dev);
|
||||
int retval;
|
||||
pr_debug("%s: %s\n", dev->name, __FUNCTION__);
|
||||
|
||||
@ -891,8 +922,8 @@ static int bf537mac_open(struct net_device *dev)
|
||||
phy_start(lp->phydev);
|
||||
phy_write(lp->phydev, MII_BMCR, BMCR_RESET);
|
||||
setup_system_regs(dev);
|
||||
bf537mac_disable();
|
||||
bf537mac_enable();
|
||||
bfin_mac_disable();
|
||||
bfin_mac_enable();
|
||||
pr_debug("hardware init finished\n");
|
||||
netif_start_queue(dev);
|
||||
netif_carrier_on(dev);
|
||||
@ -906,9 +937,9 @@ static int bf537mac_open(struct net_device *dev)
|
||||
* and not talk to the outside world. Caused by
|
||||
* an 'ifconfig ethX down'
|
||||
*/
|
||||
static int bf537mac_close(struct net_device *dev)
|
||||
static int bfin_mac_close(struct net_device *dev)
|
||||
{
|
||||
struct bf537mac_local *lp = netdev_priv(dev);
|
||||
struct bfin_mac_local *lp = netdev_priv(dev);
|
||||
pr_debug("%s: %s\n", dev->name, __FUNCTION__);
|
||||
|
||||
netif_stop_queue(dev);
|
||||
@ -918,7 +949,7 @@ static int bf537mac_close(struct net_device *dev)
|
||||
phy_write(lp->phydev, MII_BMCR, BMCR_PDOWN);
|
||||
|
||||
/* clear everything */
|
||||
bf537mac_shutdown(dev);
|
||||
bfin_mac_shutdown(dev);
|
||||
|
||||
/* free the rx/tx buffers */
|
||||
desc_list_free();
|
||||
@ -926,46 +957,59 @@ static int bf537mac_close(struct net_device *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init bf537mac_probe(struct net_device *dev)
|
||||
static int __init bfin_mac_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct bf537mac_local *lp = netdev_priv(dev);
|
||||
int retval;
|
||||
int i;
|
||||
struct net_device *ndev;
|
||||
struct bfin_mac_local *lp;
|
||||
int rc, i;
|
||||
|
||||
ndev = alloc_etherdev(sizeof(struct bfin_mac_local));
|
||||
if (!ndev) {
|
||||
dev_err(&pdev->dev, "Cannot allocate net device!\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
SET_NETDEV_DEV(ndev, &pdev->dev);
|
||||
platform_set_drvdata(pdev, ndev);
|
||||
lp = netdev_priv(ndev);
|
||||
|
||||
/* Grab the MAC address in the MAC */
|
||||
*(__le32 *) (&(dev->dev_addr[0])) = cpu_to_le32(bfin_read_EMAC_ADDRLO());
|
||||
*(__le16 *) (&(dev->dev_addr[4])) = cpu_to_le16((u16) bfin_read_EMAC_ADDRHI());
|
||||
*(__le32 *) (&(ndev->dev_addr[0])) = cpu_to_le32(bfin_read_EMAC_ADDRLO());
|
||||
*(__le16 *) (&(ndev->dev_addr[4])) = cpu_to_le16((u16) bfin_read_EMAC_ADDRHI());
|
||||
|
||||
/* probe mac */
|
||||
/*todo: how to proble? which is revision_register */
|
||||
bfin_write_EMAC_ADDRLO(0x12345678);
|
||||
if (bfin_read_EMAC_ADDRLO() != 0x12345678) {
|
||||
pr_debug("can't detect bf537 mac!\n");
|
||||
retval = -ENODEV;
|
||||
goto err_out;
|
||||
dev_err(&pdev->dev, "Cannot detect Blackfin on-chip ethernet MAC controller!\n");
|
||||
rc = -ENODEV;
|
||||
goto out_err_probe_mac;
|
||||
}
|
||||
|
||||
/* set the GPIO pins to Ethernet mode */
|
||||
retval = setup_pin_mux(1);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
/*Is it valid? (Did bootloader initialize it?) */
|
||||
if (!is_valid_ether_addr(dev->dev_addr)) {
|
||||
/* Grab the MAC from the board somehow - this is done in the
|
||||
arch/blackfin/mach-bf537/boards/eth_mac.c */
|
||||
bfin_get_ether_addr(dev->dev_addr);
|
||||
rc = peripheral_request_list(pin_req, DRV_NAME);
|
||||
if (rc) {
|
||||
dev_err(&pdev->dev, "Requesting peripherals failed!\n");
|
||||
rc = -EFAULT;
|
||||
goto out_err_setup_pin_mux;
|
||||
}
|
||||
|
||||
/*
|
||||
* Is it valid? (Did bootloader initialize it?)
|
||||
* Grab the MAC from the board somehow
|
||||
* this is done in the arch/blackfin/mach-bfxxx/boards/eth_mac.c
|
||||
*/
|
||||
if (!is_valid_ether_addr(ndev->dev_addr))
|
||||
bfin_get_ether_addr(ndev->dev_addr);
|
||||
|
||||
/* If still not valid, get a random one */
|
||||
if (!is_valid_ether_addr(dev->dev_addr)) {
|
||||
random_ether_addr(dev->dev_addr);
|
||||
}
|
||||
if (!is_valid_ether_addr(ndev->dev_addr))
|
||||
random_ether_addr(ndev->dev_addr);
|
||||
|
||||
setup_mac_addr(dev->dev_addr);
|
||||
setup_mac_addr(ndev->dev_addr);
|
||||
|
||||
/* MDIO bus initial */
|
||||
lp->mii_bus.priv = dev;
|
||||
lp->mii_bus.priv = ndev;
|
||||
lp->mii_bus.read = mdiobus_read;
|
||||
lp->mii_bus.write = mdiobus_write;
|
||||
lp->mii_bus.reset = mdiobus_reset;
|
||||
@ -975,86 +1019,86 @@ static int __init bf537mac_probe(struct net_device *dev)
|
||||
for (i = 0; i < PHY_MAX_ADDR; ++i)
|
||||
lp->mii_bus.irq[i] = PHY_POLL;
|
||||
|
||||
mdiobus_register(&lp->mii_bus);
|
||||
rc = mdiobus_register(&lp->mii_bus);
|
||||
if (rc) {
|
||||
dev_err(&pdev->dev, "Cannot register MDIO bus!\n");
|
||||
goto out_err_mdiobus_register;
|
||||
}
|
||||
|
||||
retval = mii_probe(dev);
|
||||
if (retval)
|
||||
return retval;
|
||||
rc = mii_probe(ndev);
|
||||
if (rc) {
|
||||
dev_err(&pdev->dev, "MII Probe failed!\n");
|
||||
goto out_err_mii_probe;
|
||||
}
|
||||
|
||||
/* Fill in the fields of the device structure with ethernet values. */
|
||||
ether_setup(dev);
|
||||
ether_setup(ndev);
|
||||
|
||||
dev->open = bf537mac_open;
|
||||
dev->stop = bf537mac_close;
|
||||
dev->hard_start_xmit = bf537mac_hard_start_xmit;
|
||||
dev->set_mac_address = bf537mac_set_mac_address;
|
||||
dev->tx_timeout = bf537mac_timeout;
|
||||
dev->set_multicast_list = bf537mac_set_multicast_list;
|
||||
ndev->open = bfin_mac_open;
|
||||
ndev->stop = bfin_mac_close;
|
||||
ndev->hard_start_xmit = bfin_mac_hard_start_xmit;
|
||||
ndev->set_mac_address = bfin_mac_set_mac_address;
|
||||
ndev->tx_timeout = bfin_mac_timeout;
|
||||
ndev->set_multicast_list = bfin_mac_set_multicast_list;
|
||||
#ifdef CONFIG_NET_POLL_CONTROLLER
|
||||
dev->poll_controller = bf537mac_poll;
|
||||
ndev->poll_controller = bfin_mac_poll;
|
||||
#endif
|
||||
ndev->ethtool_ops = &bfin_mac_ethtool_ops;
|
||||
|
||||
spin_lock_init(&lp->lock);
|
||||
|
||||
/* now, enable interrupts */
|
||||
/* register irq handler */
|
||||
if (request_irq
|
||||
(IRQ_MAC_RX, bf537mac_interrupt, IRQF_DISABLED | IRQF_SHARED,
|
||||
"EMAC_RX", dev)) {
|
||||
printk(KERN_WARNING DRV_NAME
|
||||
": Unable to attach BlackFin MAC RX interrupt\n");
|
||||
return -EBUSY;
|
||||
rc = request_irq(IRQ_MAC_RX, bfin_mac_interrupt,
|
||||
IRQF_DISABLED | IRQF_SHARED, "EMAC_RX", ndev);
|
||||
if (rc) {
|
||||
dev_err(&pdev->dev, "Cannot request Blackfin MAC RX IRQ!\n");
|
||||
rc = -EBUSY;
|
||||
goto out_err_request_irq;
|
||||
}
|
||||
|
||||
rc = register_netdev(ndev);
|
||||
if (rc) {
|
||||
dev_err(&pdev->dev, "Cannot register net device!\n");
|
||||
goto out_err_reg_ndev;
|
||||
}
|
||||
|
||||
retval = register_netdev(dev);
|
||||
if (retval == 0) {
|
||||
/* now, print out the card info, in a short format.. */
|
||||
printk(KERN_INFO "%s: Version %s, %s\n",
|
||||
DRV_NAME, DRV_VERSION, DRV_DESC);
|
||||
}
|
||||
|
||||
err_out:
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int bfin_mac_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct net_device *ndev;
|
||||
|
||||
ndev = alloc_etherdev(sizeof(struct bf537mac_local));
|
||||
if (!ndev) {
|
||||
printk(KERN_WARNING DRV_NAME ": could not allocate device\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
SET_NETDEV_DEV(ndev, &pdev->dev);
|
||||
|
||||
platform_set_drvdata(pdev, ndev);
|
||||
|
||||
if (bf537mac_probe(ndev) != 0) {
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
free_netdev(ndev);
|
||||
printk(KERN_WARNING DRV_NAME ": not found\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
dev_info(&pdev->dev, "%s, Version %s\n", DRV_DESC, DRV_VERSION);
|
||||
|
||||
return 0;
|
||||
|
||||
out_err_reg_ndev:
|
||||
free_irq(IRQ_MAC_RX, ndev);
|
||||
out_err_request_irq:
|
||||
out_err_mii_probe:
|
||||
mdiobus_unregister(&lp->mii_bus);
|
||||
out_err_mdiobus_register:
|
||||
peripheral_free_list(pin_req);
|
||||
out_err_setup_pin_mux:
|
||||
out_err_probe_mac:
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
free_netdev(ndev);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int bfin_mac_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct net_device *ndev = platform_get_drvdata(pdev);
|
||||
struct bfin_mac_local *lp = netdev_priv(ndev);
|
||||
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
|
||||
mdiobus_unregister(&lp->mii_bus);
|
||||
|
||||
unregister_netdev(ndev);
|
||||
|
||||
free_irq(IRQ_MAC_RX, ndev);
|
||||
|
||||
free_netdev(ndev);
|
||||
|
||||
setup_pin_mux(0);
|
||||
peripheral_free_list(pin_req);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1065,7 +1109,7 @@ static int bfin_mac_suspend(struct platform_device *pdev, pm_message_t mesg)
|
||||
struct net_device *net_dev = platform_get_drvdata(pdev);
|
||||
|
||||
if (netif_running(net_dev))
|
||||
bf537mac_close(net_dev);
|
||||
bfin_mac_close(net_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1075,7 +1119,7 @@ static int bfin_mac_resume(struct platform_device *pdev)
|
||||
struct net_device *net_dev = platform_get_drvdata(pdev);
|
||||
|
||||
if (netif_running(net_dev))
|
||||
bf537mac_open(net_dev);
|
||||
bfin_mac_open(net_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -49,7 +49,7 @@ struct net_dma_desc_tx {
|
||||
struct status_area_tx status;
|
||||
};
|
||||
|
||||
struct bf537mac_local {
|
||||
struct bfin_mac_local {
|
||||
/*
|
||||
* these are things that the kernel wants me to keep, so users
|
||||
* can find out semi-useless statistics of how well the card is
|
||||
|
@ -633,7 +633,7 @@ static void __init printEEPROMInfo(struct net_device *dev)
|
||||
printk(KERN_DEBUG " PC: %d\n", GetBit(Word,ee_PC));
|
||||
printk(KERN_DEBUG " TPE/AUI: %d\n", GetBit(Word,ee_TPE_AUI));
|
||||
printk(KERN_DEBUG " Jabber: %d\n", GetBit(Word,ee_Jabber));
|
||||
printk(KERN_DEBUG " AutoPort: %d\n", GetBit(!Word,ee_Jabber));
|
||||
printk(KERN_DEBUG " AutoPort: %d\n", !GetBit(Word,ee_AutoPort));
|
||||
printk(KERN_DEBUG " Duplex: %d\n", GetBit(Word,ee_Duplex));
|
||||
}
|
||||
|
||||
|
@ -131,8 +131,6 @@ static void free_skb_resources(struct gfar_private *priv);
|
||||
static void gfar_set_multi(struct net_device *dev);
|
||||
static void gfar_set_hash_for_addr(struct net_device *dev, u8 *addr);
|
||||
static void gfar_configure_serdes(struct net_device *dev);
|
||||
extern int gfar_local_mdio_write(struct gfar_mii __iomem *regs, int mii_id, int regnum, u16 value);
|
||||
extern int gfar_local_mdio_read(struct gfar_mii __iomem *regs, int mii_id, int regnum);
|
||||
#ifdef CONFIG_GFAR_NAPI
|
||||
static int gfar_poll(struct napi_struct *napi, int budget);
|
||||
#endif
|
||||
@ -477,24 +475,30 @@ static int init_phy(struct net_device *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize TBI PHY interface for communicating with the
|
||||
* SERDES lynx PHY on the chip. We communicate with this PHY
|
||||
* through the MDIO bus on each controller, treating it as a
|
||||
* "normal" PHY at the address found in the TBIPA register. We assume
|
||||
* that the TBIPA register is valid. Either the MDIO bus code will set
|
||||
* it to a value that doesn't conflict with other PHYs on the bus, or the
|
||||
* value doesn't matter, as there are no other PHYs on the bus.
|
||||
*/
|
||||
static void gfar_configure_serdes(struct net_device *dev)
|
||||
{
|
||||
struct gfar_private *priv = netdev_priv(dev);
|
||||
struct gfar_mii __iomem *regs =
|
||||
(void __iomem *)&priv->regs->gfar_mii_regs;
|
||||
int tbipa = gfar_read(&priv->regs->tbipa);
|
||||
|
||||
/* Initialise TBI i/f to communicate with serdes (lynx phy) */
|
||||
/* Single clk mode, mii mode off(for serdes communication) */
|
||||
gfar_local_mdio_write(regs, tbipa, MII_TBICON, TBICON_CLK_SELECT);
|
||||
|
||||
/* Single clk mode, mii mode off(for aerdes communication) */
|
||||
gfar_local_mdio_write(regs, TBIPA_VALUE, MII_TBICON, TBICON_CLK_SELECT);
|
||||
|
||||
/* Supported pause and full-duplex, no half-duplex */
|
||||
gfar_local_mdio_write(regs, TBIPA_VALUE, MII_ADVERTISE,
|
||||
gfar_local_mdio_write(regs, tbipa, MII_ADVERTISE,
|
||||
ADVERTISE_1000XFULL | ADVERTISE_1000XPAUSE |
|
||||
ADVERTISE_1000XPSE_ASYM);
|
||||
|
||||
/* ANEG enable, restart ANEG, full duplex mode, speed[1] set */
|
||||
gfar_local_mdio_write(regs, TBIPA_VALUE, MII_BMCR, BMCR_ANENABLE |
|
||||
gfar_local_mdio_write(regs, tbipa, MII_BMCR, BMCR_ANENABLE |
|
||||
BMCR_ANRESTART | BMCR_FULLDPLX | BMCR_SPEED1000);
|
||||
}
|
||||
|
||||
@ -541,9 +545,6 @@ static void init_registers(struct net_device *dev)
|
||||
|
||||
/* Initialize the Minimum Frame Length Register */
|
||||
gfar_write(&priv->regs->minflr, MINFLR_INIT_SETTINGS);
|
||||
|
||||
/* Assign the TBI an address which won't conflict with the PHYs */
|
||||
gfar_write(&priv->regs->tbipa, TBIPA_VALUE);
|
||||
}
|
||||
|
||||
|
||||
|
@ -137,7 +137,6 @@ extern const char gfar_driver_version[];
|
||||
#define DEFAULT_RXCOUNT 0
|
||||
#endif /* CONFIG_GFAR_NAPI */
|
||||
|
||||
#define TBIPA_VALUE 0x1f
|
||||
#define MIIMCFG_INIT_VALUE 0x00000007
|
||||
#define MIIMCFG_RESET 0x80000000
|
||||
#define MIIMIND_BUSY 0x00000001
|
||||
|
@ -78,7 +78,6 @@ int gfar_local_mdio_write(struct gfar_mii __iomem *regs, int mii_id,
|
||||
* same as system mdio bus, used for controlling the external PHYs, for eg.
|
||||
*/
|
||||
int gfar_local_mdio_read(struct gfar_mii __iomem *regs, int mii_id, int regnum)
|
||||
|
||||
{
|
||||
u16 value;
|
||||
|
||||
@ -122,7 +121,7 @@ int gfar_mdio_read(struct mii_bus *bus, int mii_id, int regnum)
|
||||
}
|
||||
|
||||
/* Reset the MIIM registers, and wait for the bus to free */
|
||||
int gfar_mdio_reset(struct mii_bus *bus)
|
||||
static int gfar_mdio_reset(struct mii_bus *bus)
|
||||
{
|
||||
struct gfar_mii __iomem *regs = (void __iomem *)bus->priv;
|
||||
unsigned int timeout = PHY_INIT_TIMEOUT;
|
||||
@ -152,14 +151,15 @@ int gfar_mdio_reset(struct mii_bus *bus)
|
||||
}
|
||||
|
||||
|
||||
int gfar_mdio_probe(struct device *dev)
|
||||
static int gfar_mdio_probe(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct gianfar_mdio_data *pdata;
|
||||
struct gfar_mii __iomem *regs;
|
||||
struct gfar __iomem *enet_regs;
|
||||
struct mii_bus *new_bus;
|
||||
struct resource *r;
|
||||
int err = 0;
|
||||
int i, err = 0;
|
||||
|
||||
if (NULL == dev)
|
||||
return -EINVAL;
|
||||
@ -199,6 +199,34 @@ int gfar_mdio_probe(struct device *dev)
|
||||
new_bus->dev = dev;
|
||||
dev_set_drvdata(dev, new_bus);
|
||||
|
||||
/*
|
||||
* This is mildly evil, but so is our hardware for doing this.
|
||||
* Also, we have to cast back to struct gfar_mii because of
|
||||
* definition weirdness done in gianfar.h.
|
||||
*/
|
||||
enet_regs = (struct gfar __iomem *)
|
||||
((char *)regs - offsetof(struct gfar, gfar_mii_regs));
|
||||
|
||||
/* Scan the bus, looking for an empty spot for TBIPA */
|
||||
gfar_write(&enet_regs->tbipa, 0);
|
||||
for (i = PHY_MAX_ADDR; i > 0; i--) {
|
||||
u32 phy_id;
|
||||
int r;
|
||||
|
||||
r = get_phy_id(new_bus, i, &phy_id);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
if (phy_id == 0xffffffff)
|
||||
break;
|
||||
}
|
||||
|
||||
/* The bus is full. We don't support using 31 PHYs, sorry */
|
||||
if (i == 0)
|
||||
return -EBUSY;
|
||||
|
||||
gfar_write(&enet_regs->tbipa, i);
|
||||
|
||||
err = mdiobus_register(new_bus);
|
||||
|
||||
if (0 != err) {
|
||||
@ -218,7 +246,7 @@ reg_map_fail:
|
||||
}
|
||||
|
||||
|
||||
int gfar_mdio_remove(struct device *dev)
|
||||
static int gfar_mdio_remove(struct device *dev)
|
||||
{
|
||||
struct mii_bus *bus = dev_get_drvdata(dev);
|
||||
|
||||
|
@ -41,6 +41,9 @@ struct gfar_mii {
|
||||
|
||||
int gfar_mdio_read(struct mii_bus *bus, int mii_id, int regnum);
|
||||
int gfar_mdio_write(struct mii_bus *bus, int mii_id, int regnum, u16 value);
|
||||
int gfar_local_mdio_write(struct gfar_mii __iomem *regs, int mii_id,
|
||||
int regnum, u16 value);
|
||||
int gfar_local_mdio_read(struct gfar_mii __iomem *regs, int mii_id, int regnum);
|
||||
int __init gfar_mdio_init(void);
|
||||
void gfar_mdio_exit(void);
|
||||
#endif /* GIANFAR_PHY_H */
|
||||
|
@ -48,7 +48,7 @@ config VITESSE_PHY
|
||||
config SMSC_PHY
|
||||
tristate "Drivers for SMSC PHYs"
|
||||
---help---
|
||||
Currently supports the LAN83C185 PHY
|
||||
Currently supports the LAN83C185, LAN8187 and LAN8700 PHYs
|
||||
|
||||
config BROADCOM_PHY
|
||||
tristate "Drivers for Broadcom PHYs"
|
||||
|
@ -256,7 +256,7 @@ void phy_prepare_link(struct phy_device *phydev,
|
||||
/**
|
||||
* phy_connect - connect an ethernet device to a PHY device
|
||||
* @dev: the network device to connect
|
||||
* @phy_id: the PHY device to connect
|
||||
* @bus_id: the id string of the PHY device to connect
|
||||
* @handler: callback function for state change notifications
|
||||
* @flags: PHY device's dev_flags
|
||||
* @interface: PHY device's interface
|
||||
|
@ -12,6 +12,8 @@
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* Support added for SMSC LAN8187 and LAN8700 by steve.glendinning@smsc.com
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
@ -38,7 +40,7 @@
|
||||
(MII_LAN83C185_ISF_INT6 | MII_LAN83C185_ISF_INT4)
|
||||
|
||||
|
||||
static int lan83c185_config_intr(struct phy_device *phydev)
|
||||
static int smsc_phy_config_intr(struct phy_device *phydev)
|
||||
{
|
||||
int rc = phy_write (phydev, MII_LAN83C185_IM,
|
||||
((PHY_INTERRUPT_ENABLED == phydev->interrupts)
|
||||
@ -48,16 +50,16 @@ static int lan83c185_config_intr(struct phy_device *phydev)
|
||||
return rc < 0 ? rc : 0;
|
||||
}
|
||||
|
||||
static int lan83c185_ack_interrupt(struct phy_device *phydev)
|
||||
static int smsc_phy_ack_interrupt(struct phy_device *phydev)
|
||||
{
|
||||
int rc = phy_read (phydev, MII_LAN83C185_ISF);
|
||||
|
||||
return rc < 0 ? rc : 0;
|
||||
}
|
||||
|
||||
static int lan83c185_config_init(struct phy_device *phydev)
|
||||
static int smsc_phy_config_init(struct phy_device *phydev)
|
||||
{
|
||||
return lan83c185_ack_interrupt (phydev);
|
||||
return smsc_phy_ack_interrupt (phydev);
|
||||
}
|
||||
|
||||
|
||||
@ -73,22 +75,87 @@ static struct phy_driver lan83c185_driver = {
|
||||
/* basic functions */
|
||||
.config_aneg = genphy_config_aneg,
|
||||
.read_status = genphy_read_status,
|
||||
.config_init = lan83c185_config_init,
|
||||
.config_init = smsc_phy_config_init,
|
||||
|
||||
/* IRQ related */
|
||||
.ack_interrupt = lan83c185_ack_interrupt,
|
||||
.config_intr = lan83c185_config_intr,
|
||||
.ack_interrupt = smsc_phy_ack_interrupt,
|
||||
.config_intr = smsc_phy_config_intr,
|
||||
|
||||
.driver = { .owner = THIS_MODULE, }
|
||||
};
|
||||
|
||||
static struct phy_driver lan8187_driver = {
|
||||
.phy_id = 0x0007c0b0, /* OUI=0x00800f, Model#=0x0b */
|
||||
.phy_id_mask = 0xfffffff0,
|
||||
.name = "SMSC LAN8187",
|
||||
|
||||
.features = (PHY_BASIC_FEATURES | SUPPORTED_Pause
|
||||
| SUPPORTED_Asym_Pause),
|
||||
.flags = PHY_HAS_INTERRUPT | PHY_HAS_MAGICANEG,
|
||||
|
||||
/* basic functions */
|
||||
.config_aneg = genphy_config_aneg,
|
||||
.read_status = genphy_read_status,
|
||||
.config_init = smsc_phy_config_init,
|
||||
|
||||
/* IRQ related */
|
||||
.ack_interrupt = smsc_phy_ack_interrupt,
|
||||
.config_intr = smsc_phy_config_intr,
|
||||
|
||||
.driver = { .owner = THIS_MODULE, }
|
||||
};
|
||||
|
||||
static struct phy_driver lan8700_driver = {
|
||||
.phy_id = 0x0007c0c0, /* OUI=0x00800f, Model#=0x0c */
|
||||
.phy_id_mask = 0xfffffff0,
|
||||
.name = "SMSC LAN8700",
|
||||
|
||||
.features = (PHY_BASIC_FEATURES | SUPPORTED_Pause
|
||||
| SUPPORTED_Asym_Pause),
|
||||
.flags = PHY_HAS_INTERRUPT | PHY_HAS_MAGICANEG,
|
||||
|
||||
/* basic functions */
|
||||
.config_aneg = genphy_config_aneg,
|
||||
.read_status = genphy_read_status,
|
||||
.config_init = smsc_phy_config_init,
|
||||
|
||||
/* IRQ related */
|
||||
.ack_interrupt = smsc_phy_ack_interrupt,
|
||||
.config_intr = smsc_phy_config_intr,
|
||||
|
||||
.driver = { .owner = THIS_MODULE, }
|
||||
};
|
||||
|
||||
static int __init smsc_init(void)
|
||||
{
|
||||
return phy_driver_register (&lan83c185_driver);
|
||||
int ret;
|
||||
|
||||
ret = phy_driver_register (&lan83c185_driver);
|
||||
if (ret)
|
||||
goto err1;
|
||||
|
||||
ret = phy_driver_register (&lan8187_driver);
|
||||
if (ret)
|
||||
goto err2;
|
||||
|
||||
ret = phy_driver_register (&lan8700_driver);
|
||||
if (ret)
|
||||
goto err3;
|
||||
|
||||
return 0;
|
||||
|
||||
err3:
|
||||
phy_driver_unregister (&lan8187_driver);
|
||||
err2:
|
||||
phy_driver_unregister (&lan83c185_driver);
|
||||
err1:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __exit smsc_exit(void)
|
||||
{
|
||||
phy_driver_unregister (&lan8700_driver);
|
||||
phy_driver_unregister (&lan8187_driver);
|
||||
phy_driver_unregister (&lan83c185_driver);
|
||||
}
|
||||
|
||||
|
@ -1617,6 +1617,7 @@ rtl8169_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
|
||||
SET_NETDEV_DEV(dev, &pdev->dev);
|
||||
tp = netdev_priv(dev);
|
||||
tp->dev = dev;
|
||||
tp->pci_dev = pdev;
|
||||
tp->msg_enable = netif_msg_init(debug.msg_enable, R8169_MSG_DEFAULT);
|
||||
|
||||
/* enable device (incl. PCI PM wakeup and hotplug setup) */
|
||||
@ -1705,18 +1706,18 @@ rtl8169_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
|
||||
|
||||
rtl8169_print_mac_version(tp);
|
||||
|
||||
for (i = ARRAY_SIZE(rtl_chip_info) - 1; i >= 0; i--) {
|
||||
for (i = 0; i < ARRAY_SIZE(rtl_chip_info); i++) {
|
||||
if (tp->mac_version == rtl_chip_info[i].mac_version)
|
||||
break;
|
||||
}
|
||||
if (i < 0) {
|
||||
if (i == ARRAY_SIZE(rtl_chip_info)) {
|
||||
/* Unknown chip: assume array element #0, original RTL-8169 */
|
||||
if (netif_msg_probe(tp)) {
|
||||
dev_printk(KERN_DEBUG, &pdev->dev,
|
||||
"unknown chip version, assuming %s\n",
|
||||
rtl_chip_info[0].name);
|
||||
}
|
||||
i++;
|
||||
i = 0;
|
||||
}
|
||||
tp->chipset = i;
|
||||
|
||||
@ -1777,7 +1778,6 @@ rtl8169_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
|
||||
#endif
|
||||
|
||||
tp->intr_mask = 0xffff;
|
||||
tp->pci_dev = pdev;
|
||||
tp->mmio_addr = ioaddr;
|
||||
tp->align = cfg->align;
|
||||
tp->hw_start = cfg->hw_start;
|
||||
|
@ -86,7 +86,7 @@
|
||||
#include "s2io.h"
|
||||
#include "s2io-regs.h"
|
||||
|
||||
#define DRV_VERSION "2.0.26.22"
|
||||
#define DRV_VERSION "2.0.26.23"
|
||||
|
||||
/* S2io Driver name & version. */
|
||||
static char s2io_driver_name[] = "Neterion";
|
||||
@ -809,6 +809,7 @@ static int init_shared_mem(struct s2io_nic *nic)
|
||||
config->rx_cfg[i].num_rxd - 1;
|
||||
mac_control->rings[i].nic = nic;
|
||||
mac_control->rings[i].ring_no = i;
|
||||
mac_control->rings[i].lro = lro_enable;
|
||||
|
||||
blk_cnt = config->rx_cfg[i].num_rxd /
|
||||
(rxd_count[nic->rxd_mode] + 1);
|
||||
@ -1560,113 +1561,112 @@ static int init_nic(struct s2io_nic *nic)
|
||||
writeq(val64, &bar0->tx_fifo_partition_0);
|
||||
|
||||
/* Filling the Rx round robin registers as per the
|
||||
* number of Rings and steering based on QoS.
|
||||
* number of Rings and steering based on QoS with
|
||||
* equal priority.
|
||||
*/
|
||||
switch (config->rx_ring_num) {
|
||||
case 1:
|
||||
val64 = 0x0;
|
||||
writeq(val64, &bar0->rx_w_round_robin_0);
|
||||
writeq(val64, &bar0->rx_w_round_robin_1);
|
||||
writeq(val64, &bar0->rx_w_round_robin_2);
|
||||
writeq(val64, &bar0->rx_w_round_robin_3);
|
||||
writeq(val64, &bar0->rx_w_round_robin_4);
|
||||
|
||||
val64 = 0x8080808080808080ULL;
|
||||
writeq(val64, &bar0->rts_qos_steering);
|
||||
break;
|
||||
case 2:
|
||||
val64 = 0x0000010000010000ULL;
|
||||
val64 = 0x0001000100010001ULL;
|
||||
writeq(val64, &bar0->rx_w_round_robin_0);
|
||||
val64 = 0x0100000100000100ULL;
|
||||
writeq(val64, &bar0->rx_w_round_robin_1);
|
||||
val64 = 0x0001000001000001ULL;
|
||||
writeq(val64, &bar0->rx_w_round_robin_2);
|
||||
val64 = 0x0000010000010000ULL;
|
||||
writeq(val64, &bar0->rx_w_round_robin_3);
|
||||
val64 = 0x0100000000000000ULL;
|
||||
val64 = 0x0001000100000000ULL;
|
||||
writeq(val64, &bar0->rx_w_round_robin_4);
|
||||
|
||||
val64 = 0x8080808040404040ULL;
|
||||
writeq(val64, &bar0->rts_qos_steering);
|
||||
break;
|
||||
case 3:
|
||||
val64 = 0x0001000102000001ULL;
|
||||
val64 = 0x0001020001020001ULL;
|
||||
writeq(val64, &bar0->rx_w_round_robin_0);
|
||||
val64 = 0x0001020000010001ULL;
|
||||
val64 = 0x0200010200010200ULL;
|
||||
writeq(val64, &bar0->rx_w_round_robin_1);
|
||||
val64 = 0x0200000100010200ULL;
|
||||
val64 = 0x0102000102000102ULL;
|
||||
writeq(val64, &bar0->rx_w_round_robin_2);
|
||||
val64 = 0x0001000102000001ULL;
|
||||
val64 = 0x0001020001020001ULL;
|
||||
writeq(val64, &bar0->rx_w_round_robin_3);
|
||||
val64 = 0x0001020000000000ULL;
|
||||
val64 = 0x0200010200000000ULL;
|
||||
writeq(val64, &bar0->rx_w_round_robin_4);
|
||||
|
||||
val64 = 0x8080804040402020ULL;
|
||||
writeq(val64, &bar0->rts_qos_steering);
|
||||
break;
|
||||
case 4:
|
||||
val64 = 0x0001020300010200ULL;
|
||||
val64 = 0x0001020300010203ULL;
|
||||
writeq(val64, &bar0->rx_w_round_robin_0);
|
||||
val64 = 0x0100000102030001ULL;
|
||||
writeq(val64, &bar0->rx_w_round_robin_1);
|
||||
val64 = 0x0200010000010203ULL;
|
||||
writeq(val64, &bar0->rx_w_round_robin_2);
|
||||
val64 = 0x0001020001000001ULL;
|
||||
writeq(val64, &bar0->rx_w_round_robin_3);
|
||||
val64 = 0x0203000100000000ULL;
|
||||
val64 = 0x0001020300000000ULL;
|
||||
writeq(val64, &bar0->rx_w_round_robin_4);
|
||||
|
||||
val64 = 0x8080404020201010ULL;
|
||||
writeq(val64, &bar0->rts_qos_steering);
|
||||
break;
|
||||
case 5:
|
||||
val64 = 0x0001000203000102ULL;
|
||||
val64 = 0x0001020304000102ULL;
|
||||
writeq(val64, &bar0->rx_w_round_robin_0);
|
||||
val64 = 0x0001020001030004ULL;
|
||||
val64 = 0x0304000102030400ULL;
|
||||
writeq(val64, &bar0->rx_w_round_robin_1);
|
||||
val64 = 0x0001000203000102ULL;
|
||||
val64 = 0x0102030400010203ULL;
|
||||
writeq(val64, &bar0->rx_w_round_robin_2);
|
||||
val64 = 0x0001020001030004ULL;
|
||||
val64 = 0x0400010203040001ULL;
|
||||
writeq(val64, &bar0->rx_w_round_robin_3);
|
||||
val64 = 0x0001000000000000ULL;
|
||||
val64 = 0x0203040000000000ULL;
|
||||
writeq(val64, &bar0->rx_w_round_robin_4);
|
||||
|
||||
val64 = 0x8080404020201008ULL;
|
||||
writeq(val64, &bar0->rts_qos_steering);
|
||||
break;
|
||||
case 6:
|
||||
val64 = 0x0001020304000102ULL;
|
||||
val64 = 0x0001020304050001ULL;
|
||||
writeq(val64, &bar0->rx_w_round_robin_0);
|
||||
val64 = 0x0304050001020001ULL;
|
||||
val64 = 0x0203040500010203ULL;
|
||||
writeq(val64, &bar0->rx_w_round_robin_1);
|
||||
val64 = 0x0203000100000102ULL;
|
||||
val64 = 0x0405000102030405ULL;
|
||||
writeq(val64, &bar0->rx_w_round_robin_2);
|
||||
val64 = 0x0304000102030405ULL;
|
||||
val64 = 0x0001020304050001ULL;
|
||||
writeq(val64, &bar0->rx_w_round_robin_3);
|
||||
val64 = 0x0001000200000000ULL;
|
||||
val64 = 0x0203040500000000ULL;
|
||||
writeq(val64, &bar0->rx_w_round_robin_4);
|
||||
|
||||
val64 = 0x8080404020100804ULL;
|
||||
writeq(val64, &bar0->rts_qos_steering);
|
||||
break;
|
||||
case 7:
|
||||
val64 = 0x0001020001020300ULL;
|
||||
val64 = 0x0001020304050600ULL;
|
||||
writeq(val64, &bar0->rx_w_round_robin_0);
|
||||
val64 = 0x0102030400010203ULL;
|
||||
val64 = 0x0102030405060001ULL;
|
||||
writeq(val64, &bar0->rx_w_round_robin_1);
|
||||
val64 = 0x0405060001020001ULL;
|
||||
val64 = 0x0203040506000102ULL;
|
||||
writeq(val64, &bar0->rx_w_round_robin_2);
|
||||
val64 = 0x0304050000010200ULL;
|
||||
val64 = 0x0304050600010203ULL;
|
||||
writeq(val64, &bar0->rx_w_round_robin_3);
|
||||
val64 = 0x0102030000000000ULL;
|
||||
val64 = 0x0405060000000000ULL;
|
||||
writeq(val64, &bar0->rx_w_round_robin_4);
|
||||
|
||||
val64 = 0x8080402010080402ULL;
|
||||
writeq(val64, &bar0->rts_qos_steering);
|
||||
break;
|
||||
case 8:
|
||||
val64 = 0x0001020300040105ULL;
|
||||
val64 = 0x0001020304050607ULL;
|
||||
writeq(val64, &bar0->rx_w_round_robin_0);
|
||||
val64 = 0x0200030106000204ULL;
|
||||
writeq(val64, &bar0->rx_w_round_robin_1);
|
||||
val64 = 0x0103000502010007ULL;
|
||||
writeq(val64, &bar0->rx_w_round_robin_2);
|
||||
val64 = 0x0304010002060500ULL;
|
||||
writeq(val64, &bar0->rx_w_round_robin_3);
|
||||
val64 = 0x0103020400000000ULL;
|
||||
val64 = 0x0001020300000000ULL;
|
||||
writeq(val64, &bar0->rx_w_round_robin_4);
|
||||
|
||||
val64 = 0x8040201008040201ULL;
|
||||
@ -2499,8 +2499,7 @@ static void stop_nic(struct s2io_nic *nic)
|
||||
|
||||
/**
|
||||
* fill_rx_buffers - Allocates the Rx side skbs
|
||||
* @nic: device private variable
|
||||
* @ring_no: ring number
|
||||
* @ring_info: per ring structure
|
||||
* Description:
|
||||
* The function allocates Rx side skbs and puts the physical
|
||||
* address of these buffers into the RxD buffer pointers, so that the NIC
|
||||
@ -2518,103 +2517,94 @@ static void stop_nic(struct s2io_nic *nic)
|
||||
* SUCCESS on success or an appropriate -ve value on failure.
|
||||
*/
|
||||
|
||||
static int fill_rx_buffers(struct s2io_nic *nic, int ring_no)
|
||||
static int fill_rx_buffers(struct ring_info *ring)
|
||||
{
|
||||
struct net_device *dev = nic->dev;
|
||||
struct sk_buff *skb;
|
||||
struct RxD_t *rxdp;
|
||||
int off, off1, size, block_no, block_no1;
|
||||
int off, size, block_no, block_no1;
|
||||
u32 alloc_tab = 0;
|
||||
u32 alloc_cnt;
|
||||
struct mac_info *mac_control;
|
||||
struct config_param *config;
|
||||
u64 tmp;
|
||||
struct buffAdd *ba;
|
||||
struct RxD_t *first_rxdp = NULL;
|
||||
u64 Buffer0_ptr = 0, Buffer1_ptr = 0;
|
||||
int rxd_index = 0;
|
||||
struct RxD1 *rxdp1;
|
||||
struct RxD3 *rxdp3;
|
||||
struct swStat *stats = &nic->mac_control.stats_info->sw_stat;
|
||||
struct swStat *stats = &ring->nic->mac_control.stats_info->sw_stat;
|
||||
|
||||
mac_control = &nic->mac_control;
|
||||
config = &nic->config;
|
||||
alloc_cnt = mac_control->rings[ring_no].pkt_cnt -
|
||||
atomic_read(&nic->rx_bufs_left[ring_no]);
|
||||
alloc_cnt = ring->pkt_cnt - ring->rx_bufs_left;
|
||||
|
||||
block_no1 = mac_control->rings[ring_no].rx_curr_get_info.block_index;
|
||||
off1 = mac_control->rings[ring_no].rx_curr_get_info.offset;
|
||||
block_no1 = ring->rx_curr_get_info.block_index;
|
||||
while (alloc_tab < alloc_cnt) {
|
||||
block_no = mac_control->rings[ring_no].rx_curr_put_info.
|
||||
block_index;
|
||||
off = mac_control->rings[ring_no].rx_curr_put_info.offset;
|
||||
block_no = ring->rx_curr_put_info.block_index;
|
||||
|
||||
rxdp = mac_control->rings[ring_no].
|
||||
rx_blocks[block_no].rxds[off].virt_addr;
|
||||
off = ring->rx_curr_put_info.offset;
|
||||
|
||||
if ((block_no == block_no1) && (off == off1) &&
|
||||
rxdp = ring->rx_blocks[block_no].rxds[off].virt_addr;
|
||||
|
||||
rxd_index = off + 1;
|
||||
if (block_no)
|
||||
rxd_index += (block_no * ring->rxd_count);
|
||||
|
||||
if ((block_no == block_no1) &&
|
||||
(off == ring->rx_curr_get_info.offset) &&
|
||||
(rxdp->Host_Control)) {
|
||||
DBG_PRINT(INTR_DBG, "%s: Get and Put",
|
||||
dev->name);
|
||||
ring->dev->name);
|
||||
DBG_PRINT(INTR_DBG, " info equated\n");
|
||||
goto end;
|
||||
}
|
||||
if (off && (off == rxd_count[nic->rxd_mode])) {
|
||||
mac_control->rings[ring_no].rx_curr_put_info.
|
||||
block_index++;
|
||||
if (mac_control->rings[ring_no].rx_curr_put_info.
|
||||
block_index == mac_control->rings[ring_no].
|
||||
block_count)
|
||||
mac_control->rings[ring_no].rx_curr_put_info.
|
||||
block_index = 0;
|
||||
block_no = mac_control->rings[ring_no].
|
||||
rx_curr_put_info.block_index;
|
||||
if (off == rxd_count[nic->rxd_mode])
|
||||
if (off && (off == ring->rxd_count)) {
|
||||
ring->rx_curr_put_info.block_index++;
|
||||
if (ring->rx_curr_put_info.block_index ==
|
||||
ring->block_count)
|
||||
ring->rx_curr_put_info.block_index = 0;
|
||||
block_no = ring->rx_curr_put_info.block_index;
|
||||
off = 0;
|
||||
mac_control->rings[ring_no].rx_curr_put_info.
|
||||
offset = off;
|
||||
rxdp = mac_control->rings[ring_no].
|
||||
rx_blocks[block_no].block_virt_addr;
|
||||
ring->rx_curr_put_info.offset = off;
|
||||
rxdp = ring->rx_blocks[block_no].block_virt_addr;
|
||||
DBG_PRINT(INTR_DBG, "%s: Next block at: %p\n",
|
||||
dev->name, rxdp);
|
||||
ring->dev->name, rxdp);
|
||||
|
||||
}
|
||||
|
||||
if ((rxdp->Control_1 & RXD_OWN_XENA) &&
|
||||
((nic->rxd_mode == RXD_MODE_3B) &&
|
||||
((ring->rxd_mode == RXD_MODE_3B) &&
|
||||
(rxdp->Control_2 & s2BIT(0)))) {
|
||||
mac_control->rings[ring_no].rx_curr_put_info.
|
||||
offset = off;
|
||||
ring->rx_curr_put_info.offset = off;
|
||||
goto end;
|
||||
}
|
||||
/* calculate size of skb based on ring mode */
|
||||
size = dev->mtu + HEADER_ETHERNET_II_802_3_SIZE +
|
||||
size = ring->mtu + HEADER_ETHERNET_II_802_3_SIZE +
|
||||
HEADER_802_2_SIZE + HEADER_SNAP_SIZE;
|
||||
if (nic->rxd_mode == RXD_MODE_1)
|
||||
if (ring->rxd_mode == RXD_MODE_1)
|
||||
size += NET_IP_ALIGN;
|
||||
else
|
||||
size = dev->mtu + ALIGN_SIZE + BUF0_LEN + 4;
|
||||
size = ring->mtu + ALIGN_SIZE + BUF0_LEN + 4;
|
||||
|
||||
/* allocate skb */
|
||||
skb = dev_alloc_skb(size);
|
||||
if(!skb) {
|
||||
DBG_PRINT(INFO_DBG, "%s: Out of ", dev->name);
|
||||
DBG_PRINT(INFO_DBG, "%s: Out of ", ring->dev->name);
|
||||
DBG_PRINT(INFO_DBG, "memory to allocate SKBs\n");
|
||||
if (first_rxdp) {
|
||||
wmb();
|
||||
first_rxdp->Control_1 |= RXD_OWN_XENA;
|
||||
}
|
||||
nic->mac_control.stats_info->sw_stat. \
|
||||
mem_alloc_fail_cnt++;
|
||||
stats->mem_alloc_fail_cnt++;
|
||||
|
||||
return -ENOMEM ;
|
||||
}
|
||||
nic->mac_control.stats_info->sw_stat.mem_allocated
|
||||
+= skb->truesize;
|
||||
if (nic->rxd_mode == RXD_MODE_1) {
|
||||
stats->mem_allocated += skb->truesize;
|
||||
|
||||
if (ring->rxd_mode == RXD_MODE_1) {
|
||||
/* 1 buffer mode - normal operation mode */
|
||||
rxdp1 = (struct RxD1*)rxdp;
|
||||
memset(rxdp, 0, sizeof(struct RxD1));
|
||||
skb_reserve(skb, NET_IP_ALIGN);
|
||||
rxdp1->Buffer0_ptr = pci_map_single
|
||||
(nic->pdev, skb->data, size - NET_IP_ALIGN,
|
||||
(ring->pdev, skb->data, size - NET_IP_ALIGN,
|
||||
PCI_DMA_FROMDEVICE);
|
||||
if( (rxdp1->Buffer0_ptr == 0) ||
|
||||
(rxdp1->Buffer0_ptr ==
|
||||
@ -2623,8 +2613,8 @@ static int fill_rx_buffers(struct s2io_nic *nic, int ring_no)
|
||||
|
||||
rxdp->Control_2 =
|
||||
SET_BUFFER0_SIZE_1(size - NET_IP_ALIGN);
|
||||
|
||||
} else if (nic->rxd_mode == RXD_MODE_3B) {
|
||||
rxdp->Host_Control = (unsigned long) (skb);
|
||||
} else if (ring->rxd_mode == RXD_MODE_3B) {
|
||||
/*
|
||||
* 2 buffer mode -
|
||||
* 2 buffer mode provides 128
|
||||
@ -2640,7 +2630,7 @@ static int fill_rx_buffers(struct s2io_nic *nic, int ring_no)
|
||||
rxdp3->Buffer0_ptr = Buffer0_ptr;
|
||||
rxdp3->Buffer1_ptr = Buffer1_ptr;
|
||||
|
||||
ba = &mac_control->rings[ring_no].ba[block_no][off];
|
||||
ba = &ring->ba[block_no][off];
|
||||
skb_reserve(skb, BUF0_LEN);
|
||||
tmp = (u64)(unsigned long) skb->data;
|
||||
tmp += ALIGN_SIZE;
|
||||
@ -2650,10 +2640,10 @@ static int fill_rx_buffers(struct s2io_nic *nic, int ring_no)
|
||||
|
||||
if (!(rxdp3->Buffer0_ptr))
|
||||
rxdp3->Buffer0_ptr =
|
||||
pci_map_single(nic->pdev, ba->ba_0, BUF0_LEN,
|
||||
PCI_DMA_FROMDEVICE);
|
||||
pci_map_single(ring->pdev, ba->ba_0,
|
||||
BUF0_LEN, PCI_DMA_FROMDEVICE);
|
||||
else
|
||||
pci_dma_sync_single_for_device(nic->pdev,
|
||||
pci_dma_sync_single_for_device(ring->pdev,
|
||||
(dma_addr_t) rxdp3->Buffer0_ptr,
|
||||
BUF0_LEN, PCI_DMA_FROMDEVICE);
|
||||
if( (rxdp3->Buffer0_ptr == 0) ||
|
||||
@ -2661,7 +2651,7 @@ static int fill_rx_buffers(struct s2io_nic *nic, int ring_no)
|
||||
goto pci_map_failed;
|
||||
|
||||
rxdp->Control_2 = SET_BUFFER0_SIZE_3(BUF0_LEN);
|
||||
if (nic->rxd_mode == RXD_MODE_3B) {
|
||||
if (ring->rxd_mode == RXD_MODE_3B) {
|
||||
/* Two buffer mode */
|
||||
|
||||
/*
|
||||
@ -2669,39 +2659,42 @@ static int fill_rx_buffers(struct s2io_nic *nic, int ring_no)
|
||||
* L4 payload
|
||||
*/
|
||||
rxdp3->Buffer2_ptr = pci_map_single
|
||||
(nic->pdev, skb->data, dev->mtu + 4,
|
||||
(ring->pdev, skb->data, ring->mtu + 4,
|
||||
PCI_DMA_FROMDEVICE);
|
||||
|
||||
if( (rxdp3->Buffer2_ptr == 0) ||
|
||||
(rxdp3->Buffer2_ptr == DMA_ERROR_CODE))
|
||||
goto pci_map_failed;
|
||||
|
||||
if (!rxdp3->Buffer1_ptr)
|
||||
rxdp3->Buffer1_ptr =
|
||||
pci_map_single(nic->pdev,
|
||||
pci_map_single(ring->pdev,
|
||||
ba->ba_1, BUF1_LEN,
|
||||
PCI_DMA_FROMDEVICE);
|
||||
|
||||
if( (rxdp3->Buffer1_ptr == 0) ||
|
||||
(rxdp3->Buffer1_ptr == DMA_ERROR_CODE)) {
|
||||
pci_unmap_single
|
||||
(nic->pdev,
|
||||
(dma_addr_t)rxdp3->Buffer2_ptr,
|
||||
dev->mtu + 4,
|
||||
(ring->pdev,
|
||||
(dma_addr_t)(unsigned long)
|
||||
skb->data,
|
||||
ring->mtu + 4,
|
||||
PCI_DMA_FROMDEVICE);
|
||||
goto pci_map_failed;
|
||||
}
|
||||
rxdp->Control_2 |= SET_BUFFER1_SIZE_3(1);
|
||||
rxdp->Control_2 |= SET_BUFFER2_SIZE_3
|
||||
(dev->mtu + 4);
|
||||
(ring->mtu + 4);
|
||||
}
|
||||
rxdp->Control_2 |= s2BIT(0);
|
||||
}
|
||||
rxdp->Host_Control = (unsigned long) (skb);
|
||||
}
|
||||
if (alloc_tab & ((1 << rxsync_frequency) - 1))
|
||||
rxdp->Control_1 |= RXD_OWN_XENA;
|
||||
off++;
|
||||
if (off == (rxd_count[nic->rxd_mode] + 1))
|
||||
if (off == (ring->rxd_count + 1))
|
||||
off = 0;
|
||||
mac_control->rings[ring_no].rx_curr_put_info.offset = off;
|
||||
ring->rx_curr_put_info.offset = off;
|
||||
|
||||
rxdp->Control_2 |= SET_RXD_MARKER;
|
||||
if (!(alloc_tab & ((1 << rxsync_frequency) - 1))) {
|
||||
@ -2711,7 +2704,7 @@ static int fill_rx_buffers(struct s2io_nic *nic, int ring_no)
|
||||
}
|
||||
first_rxdp = rxdp;
|
||||
}
|
||||
atomic_inc(&nic->rx_bufs_left[ring_no]);
|
||||
ring->rx_bufs_left += 1;
|
||||
alloc_tab++;
|
||||
}
|
||||
|
||||
@ -2783,7 +2776,7 @@ static void free_rxd_blk(struct s2io_nic *sp, int ring_no, int blk)
|
||||
}
|
||||
sp->mac_control.stats_info->sw_stat.mem_freed += skb->truesize;
|
||||
dev_kfree_skb(skb);
|
||||
atomic_dec(&sp->rx_bufs_left[ring_no]);
|
||||
mac_control->rings[ring_no].rx_bufs_left -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
@ -2814,7 +2807,7 @@ static void free_rx_buffers(struct s2io_nic *sp)
|
||||
mac_control->rings[i].rx_curr_get_info.block_index = 0;
|
||||
mac_control->rings[i].rx_curr_put_info.offset = 0;
|
||||
mac_control->rings[i].rx_curr_get_info.offset = 0;
|
||||
atomic_set(&sp->rx_bufs_left[i], 0);
|
||||
mac_control->rings[i].rx_bufs_left = 0;
|
||||
DBG_PRINT(INIT_DBG, "%s:Freed 0x%x Rx Buffers on ring%d\n",
|
||||
dev->name, buf_cnt, i);
|
||||
}
|
||||
@ -2864,7 +2857,7 @@ static int s2io_poll(struct napi_struct *napi, int budget)
|
||||
netif_rx_complete(dev, napi);
|
||||
|
||||
for (i = 0; i < config->rx_ring_num; i++) {
|
||||
if (fill_rx_buffers(nic, i) == -ENOMEM) {
|
||||
if (fill_rx_buffers(&mac_control->rings[i]) == -ENOMEM) {
|
||||
DBG_PRINT(INFO_DBG, "%s:Out of memory", dev->name);
|
||||
DBG_PRINT(INFO_DBG, " in Rx Poll!!\n");
|
||||
break;
|
||||
@ -2877,7 +2870,7 @@ static int s2io_poll(struct napi_struct *napi, int budget)
|
||||
|
||||
no_rx:
|
||||
for (i = 0; i < config->rx_ring_num; i++) {
|
||||
if (fill_rx_buffers(nic, i) == -ENOMEM) {
|
||||
if (fill_rx_buffers(&mac_control->rings[i]) == -ENOMEM) {
|
||||
DBG_PRINT(INFO_DBG, "%s:Out of memory", dev->name);
|
||||
DBG_PRINT(INFO_DBG, " in Rx Poll!!\n");
|
||||
break;
|
||||
@ -2928,7 +2921,7 @@ static void s2io_netpoll(struct net_device *dev)
|
||||
rx_intr_handler(&mac_control->rings[i]);
|
||||
|
||||
for (i = 0; i < config->rx_ring_num; i++) {
|
||||
if (fill_rx_buffers(nic, i) == -ENOMEM) {
|
||||
if (fill_rx_buffers(&mac_control->rings[i]) == -ENOMEM) {
|
||||
DBG_PRINT(INFO_DBG, "%s:Out of memory", dev->name);
|
||||
DBG_PRINT(INFO_DBG, " in Rx Netpoll!!\n");
|
||||
break;
|
||||
@ -2953,8 +2946,6 @@ static void s2io_netpoll(struct net_device *dev)
|
||||
*/
|
||||
static void rx_intr_handler(struct ring_info *ring_data)
|
||||
{
|
||||
struct s2io_nic *nic = ring_data->nic;
|
||||
struct net_device *dev = (struct net_device *) nic->dev;
|
||||
int get_block, put_block;
|
||||
struct rx_curr_get_info get_info, put_info;
|
||||
struct RxD_t *rxdp;
|
||||
@ -2977,33 +2968,34 @@ static void rx_intr_handler(struct ring_info *ring_data)
|
||||
*/
|
||||
if ((get_block == put_block) &&
|
||||
(get_info.offset + 1) == put_info.offset) {
|
||||
DBG_PRINT(INTR_DBG, "%s: Ring Full\n",dev->name);
|
||||
DBG_PRINT(INTR_DBG, "%s: Ring Full\n",
|
||||
ring_data->dev->name);
|
||||
break;
|
||||
}
|
||||
skb = (struct sk_buff *) ((unsigned long)rxdp->Host_Control);
|
||||
if (skb == NULL) {
|
||||
DBG_PRINT(ERR_DBG, "%s: The skb is ",
|
||||
dev->name);
|
||||
ring_data->dev->name);
|
||||
DBG_PRINT(ERR_DBG, "Null in Rx Intr\n");
|
||||
return;
|
||||
}
|
||||
if (nic->rxd_mode == RXD_MODE_1) {
|
||||
if (ring_data->rxd_mode == RXD_MODE_1) {
|
||||
rxdp1 = (struct RxD1*)rxdp;
|
||||
pci_unmap_single(nic->pdev, (dma_addr_t)
|
||||
pci_unmap_single(ring_data->pdev, (dma_addr_t)
|
||||
rxdp1->Buffer0_ptr,
|
||||
dev->mtu +
|
||||
ring_data->mtu +
|
||||
HEADER_ETHERNET_II_802_3_SIZE +
|
||||
HEADER_802_2_SIZE +
|
||||
HEADER_SNAP_SIZE,
|
||||
PCI_DMA_FROMDEVICE);
|
||||
} else if (nic->rxd_mode == RXD_MODE_3B) {
|
||||
} else if (ring_data->rxd_mode == RXD_MODE_3B) {
|
||||
rxdp3 = (struct RxD3*)rxdp;
|
||||
pci_dma_sync_single_for_cpu(nic->pdev, (dma_addr_t)
|
||||
pci_dma_sync_single_for_cpu(ring_data->pdev, (dma_addr_t)
|
||||
rxdp3->Buffer0_ptr,
|
||||
BUF0_LEN, PCI_DMA_FROMDEVICE);
|
||||
pci_unmap_single(nic->pdev, (dma_addr_t)
|
||||
pci_unmap_single(ring_data->pdev, (dma_addr_t)
|
||||
rxdp3->Buffer2_ptr,
|
||||
dev->mtu + 4,
|
||||
ring_data->mtu + 4,
|
||||
PCI_DMA_FROMDEVICE);
|
||||
}
|
||||
prefetch(skb->data);
|
||||
@ -3012,7 +3004,7 @@ static void rx_intr_handler(struct ring_info *ring_data)
|
||||
ring_data->rx_curr_get_info.offset = get_info.offset;
|
||||
rxdp = ring_data->rx_blocks[get_block].
|
||||
rxds[get_info.offset].virt_addr;
|
||||
if (get_info.offset == rxd_count[nic->rxd_mode]) {
|
||||
if (get_info.offset == rxd_count[ring_data->rxd_mode]) {
|
||||
get_info.offset = 0;
|
||||
ring_data->rx_curr_get_info.offset = get_info.offset;
|
||||
get_block++;
|
||||
@ -3022,19 +3014,21 @@ static void rx_intr_handler(struct ring_info *ring_data)
|
||||
rxdp = ring_data->rx_blocks[get_block].block_virt_addr;
|
||||
}
|
||||
|
||||
nic->pkts_to_process -= 1;
|
||||
if ((napi) && (!nic->pkts_to_process))
|
||||
if(ring_data->nic->config.napi){
|
||||
ring_data->nic->pkts_to_process -= 1;
|
||||
if (!ring_data->nic->pkts_to_process)
|
||||
break;
|
||||
}
|
||||
pkt_cnt++;
|
||||
if ((indicate_max_pkts) && (pkt_cnt > indicate_max_pkts))
|
||||
break;
|
||||
}
|
||||
if (nic->lro) {
|
||||
if (ring_data->lro) {
|
||||
/* Clear all LRO sessions before exiting */
|
||||
for (i=0; i<MAX_LRO_SESSIONS; i++) {
|
||||
struct lro *lro = &nic->lro0_n[i];
|
||||
struct lro *lro = &ring_data->lro0_n[i];
|
||||
if (lro->in_use) {
|
||||
update_L3L4_header(nic, lro);
|
||||
update_L3L4_header(ring_data->nic, lro);
|
||||
queue_rx_frame(lro->parent, lro->vlan_tag);
|
||||
clear_lro_session(lro);
|
||||
}
|
||||
@ -4333,10 +4327,10 @@ s2io_alarm_handle(unsigned long data)
|
||||
mod_timer(&sp->alarm_timer, jiffies + HZ / 2);
|
||||
}
|
||||
|
||||
static int s2io_chk_rx_buffers(struct s2io_nic *sp, int rng_n)
|
||||
static int s2io_chk_rx_buffers(struct ring_info *ring)
|
||||
{
|
||||
if (fill_rx_buffers(sp, rng_n) == -ENOMEM) {
|
||||
DBG_PRINT(INFO_DBG, "%s:Out of memory", sp->dev->name);
|
||||
if (fill_rx_buffers(ring) == -ENOMEM) {
|
||||
DBG_PRINT(INFO_DBG, "%s:Out of memory", ring->dev->name);
|
||||
DBG_PRINT(INFO_DBG, " in Rx Intr!!\n");
|
||||
}
|
||||
return 0;
|
||||
@ -4351,7 +4345,7 @@ static irqreturn_t s2io_msix_ring_handle(int irq, void *dev_id)
|
||||
return IRQ_HANDLED;
|
||||
|
||||
rx_intr_handler(ring);
|
||||
s2io_chk_rx_buffers(sp, ring->ring_no);
|
||||
s2io_chk_rx_buffers(ring);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
@ -4809,7 +4803,7 @@ static irqreturn_t s2io_isr(int irq, void *dev_id)
|
||||
*/
|
||||
if (!config->napi) {
|
||||
for (i = 0; i < config->rx_ring_num; i++)
|
||||
s2io_chk_rx_buffers(sp, i);
|
||||
s2io_chk_rx_buffers(&mac_control->rings[i]);
|
||||
}
|
||||
writeq(sp->general_int_mask, &bar0->general_int_mask);
|
||||
readl(&bar0->general_int_status);
|
||||
@ -4866,6 +4860,7 @@ static struct net_device_stats *s2io_get_stats(struct net_device *dev)
|
||||
struct s2io_nic *sp = dev->priv;
|
||||
struct mac_info *mac_control;
|
||||
struct config_param *config;
|
||||
int i;
|
||||
|
||||
|
||||
mac_control = &sp->mac_control;
|
||||
@ -4885,6 +4880,13 @@ static struct net_device_stats *s2io_get_stats(struct net_device *dev)
|
||||
sp->stats.rx_length_errors =
|
||||
le64_to_cpu(mac_control->stats_info->rmac_long_frms);
|
||||
|
||||
/* collect per-ring rx_packets and rx_bytes */
|
||||
sp->stats.rx_packets = sp->stats.rx_bytes = 0;
|
||||
for (i = 0; i < config->rx_ring_num; i++) {
|
||||
sp->stats.rx_packets += mac_control->rings[i].rx_packets;
|
||||
sp->stats.rx_bytes += mac_control->rings[i].rx_bytes;
|
||||
}
|
||||
|
||||
return (&sp->stats);
|
||||
}
|
||||
|
||||
@ -7157,7 +7159,9 @@ static int s2io_card_up(struct s2io_nic * sp)
|
||||
config = &sp->config;
|
||||
|
||||
for (i = 0; i < config->rx_ring_num; i++) {
|
||||
if ((ret = fill_rx_buffers(sp, i))) {
|
||||
mac_control->rings[i].mtu = dev->mtu;
|
||||
ret = fill_rx_buffers(&mac_control->rings[i]);
|
||||
if (ret) {
|
||||
DBG_PRINT(ERR_DBG, "%s: Out of memory in Open\n",
|
||||
dev->name);
|
||||
s2io_reset(sp);
|
||||
@ -7165,7 +7169,7 @@ static int s2io_card_up(struct s2io_nic * sp)
|
||||
return -ENOMEM;
|
||||
}
|
||||
DBG_PRINT(INFO_DBG, "Buf in ring:%d is %d:\n", i,
|
||||
atomic_read(&sp->rx_bufs_left[i]));
|
||||
mac_control->rings[i].rx_bufs_left);
|
||||
}
|
||||
|
||||
/* Initialise napi */
|
||||
@ -7300,7 +7304,7 @@ static void s2io_tx_watchdog(struct net_device *dev)
|
||||
static int rx_osm_handler(struct ring_info *ring_data, struct RxD_t * rxdp)
|
||||
{
|
||||
struct s2io_nic *sp = ring_data->nic;
|
||||
struct net_device *dev = (struct net_device *) sp->dev;
|
||||
struct net_device *dev = (struct net_device *) ring_data->dev;
|
||||
struct sk_buff *skb = (struct sk_buff *)
|
||||
((unsigned long) rxdp->Host_Control);
|
||||
int ring_no = ring_data->ring_no;
|
||||
@ -7377,19 +7381,19 @@ static int rx_osm_handler(struct ring_info *ring_data, struct RxD_t * rxdp)
|
||||
sp->mac_control.stats_info->sw_stat.mem_freed
|
||||
+= skb->truesize;
|
||||
dev_kfree_skb(skb);
|
||||
atomic_dec(&sp->rx_bufs_left[ring_no]);
|
||||
ring_data->rx_bufs_left -= 1;
|
||||
rxdp->Host_Control = 0;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Updating statistics */
|
||||
sp->stats.rx_packets++;
|
||||
ring_data->rx_packets++;
|
||||
rxdp->Host_Control = 0;
|
||||
if (sp->rxd_mode == RXD_MODE_1) {
|
||||
int len = RXD_GET_BUFFER0_SIZE_1(rxdp->Control_2);
|
||||
|
||||
sp->stats.rx_bytes += len;
|
||||
ring_data->rx_bytes += len;
|
||||
skb_put(skb, len);
|
||||
|
||||
} else if (sp->rxd_mode == RXD_MODE_3B) {
|
||||
@ -7400,13 +7404,13 @@ static int rx_osm_handler(struct ring_info *ring_data, struct RxD_t * rxdp)
|
||||
unsigned char *buff = skb_push(skb, buf0_len);
|
||||
|
||||
struct buffAdd *ba = &ring_data->ba[get_block][get_off];
|
||||
sp->stats.rx_bytes += buf0_len + buf2_len;
|
||||
ring_data->rx_bytes += buf0_len + buf2_len;
|
||||
memcpy(buff, ba->ba_0, buf0_len);
|
||||
skb_put(skb, buf2_len);
|
||||
}
|
||||
|
||||
if ((rxdp->Control_1 & TCP_OR_UDP_FRAME) && ((!sp->lro) ||
|
||||
(sp->lro && (!(rxdp->Control_1 & RXD_FRAME_IP_FRAG)))) &&
|
||||
if ((rxdp->Control_1 & TCP_OR_UDP_FRAME) && ((!ring_data->lro) ||
|
||||
(ring_data->lro && (!(rxdp->Control_1 & RXD_FRAME_IP_FRAG)))) &&
|
||||
(sp->rx_csum)) {
|
||||
l3_csum = RXD_GET_L3_CKSUM(rxdp->Control_1);
|
||||
l4_csum = RXD_GET_L4_CKSUM(rxdp->Control_1);
|
||||
@ -7417,13 +7421,13 @@ static int rx_osm_handler(struct ring_info *ring_data, struct RxD_t * rxdp)
|
||||
* a flag in the RxD.
|
||||
*/
|
||||
skb->ip_summed = CHECKSUM_UNNECESSARY;
|
||||
if (sp->lro) {
|
||||
if (ring_data->lro) {
|
||||
u32 tcp_len;
|
||||
u8 *tcp;
|
||||
int ret = 0;
|
||||
|
||||
ret = s2io_club_tcp_session(skb->data, &tcp,
|
||||
&tcp_len, &lro,
|
||||
ret = s2io_club_tcp_session(ring_data,
|
||||
skb->data, &tcp, &tcp_len, &lro,
|
||||
rxdp, sp);
|
||||
switch (ret) {
|
||||
case 3: /* Begin anew */
|
||||
@ -7486,7 +7490,7 @@ send_up:
|
||||
queue_rx_frame(skb, RXD_GET_VLAN_TAG(rxdp->Control_2));
|
||||
dev->last_rx = jiffies;
|
||||
aggregate:
|
||||
atomic_dec(&sp->rx_bufs_left[ring_no]);
|
||||
sp->mac_control.rings[ring_no].rx_bufs_left -= 1;
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
@ -7603,12 +7607,14 @@ static int s2io_verify_parm(struct pci_dev *pdev, u8 *dev_intr_type,
|
||||
tx_steering_type = NO_STEERING;
|
||||
}
|
||||
|
||||
if ( rx_ring_num > 8) {
|
||||
DBG_PRINT(ERR_DBG, "s2io: Requested number of Rx rings not "
|
||||
if (rx_ring_num > MAX_RX_RINGS) {
|
||||
DBG_PRINT(ERR_DBG, "s2io: Requested number of rx rings not "
|
||||
"supported\n");
|
||||
DBG_PRINT(ERR_DBG, "s2io: Default to 8 Rx rings\n");
|
||||
rx_ring_num = 8;
|
||||
DBG_PRINT(ERR_DBG, "s2io: Default to %d rx rings\n",
|
||||
MAX_RX_RINGS);
|
||||
rx_ring_num = MAX_RX_RINGS;
|
||||
}
|
||||
|
||||
if (*dev_intr_type != INTA)
|
||||
napi = 0;
|
||||
|
||||
@ -7836,10 +7842,15 @@ s2io_init_nic(struct pci_dev *pdev, const struct pci_device_id *pre)
|
||||
|
||||
/* Rx side parameters. */
|
||||
config->rx_ring_num = rx_ring_num;
|
||||
for (i = 0; i < MAX_RX_RINGS; i++) {
|
||||
for (i = 0; i < config->rx_ring_num; i++) {
|
||||
config->rx_cfg[i].num_rxd = rx_ring_sz[i] *
|
||||
(rxd_count[sp->rxd_mode] + 1);
|
||||
config->rx_cfg[i].ring_priority = i;
|
||||
mac_control->rings[i].rx_bufs_left = 0;
|
||||
mac_control->rings[i].rxd_mode = sp->rxd_mode;
|
||||
mac_control->rings[i].rxd_count = rxd_count[sp->rxd_mode];
|
||||
mac_control->rings[i].pdev = sp->pdev;
|
||||
mac_control->rings[i].dev = sp->dev;
|
||||
}
|
||||
|
||||
for (i = 0; i < rx_ring_num; i++) {
|
||||
@ -7854,10 +7865,6 @@ s2io_init_nic(struct pci_dev *pdev, const struct pci_device_id *pre)
|
||||
mac_control->mc_pause_threshold_q4q7 = mc_pause_threshold_q4q7;
|
||||
|
||||
|
||||
/* Initialize Ring buffer parameters. */
|
||||
for (i = 0; i < config->rx_ring_num; i++)
|
||||
atomic_set(&sp->rx_bufs_left[i], 0);
|
||||
|
||||
/* initialize the shared memory used by the NIC and the host */
|
||||
if (init_shared_mem(sp)) {
|
||||
DBG_PRINT(ERR_DBG, "%s: Memory allocation failed\n",
|
||||
@ -8077,6 +8084,9 @@ s2io_init_nic(struct pci_dev *pdev, const struct pci_device_id *pre)
|
||||
DBG_PRINT(ERR_DBG, "%s: Using %d Tx fifo(s)\n", dev->name,
|
||||
sp->config.tx_fifo_num);
|
||||
|
||||
DBG_PRINT(ERR_DBG, "%s: Using %d Rx ring(s)\n", dev->name,
|
||||
sp->config.rx_ring_num);
|
||||
|
||||
switch(sp->config.intr_type) {
|
||||
case INTA:
|
||||
DBG_PRINT(ERR_DBG, "%s: Interrupt type INTA\n", dev->name);
|
||||
@ -8391,8 +8401,9 @@ static int verify_l3_l4_lro_capable(struct lro *l_lro, struct iphdr *ip,
|
||||
}
|
||||
|
||||
static int
|
||||
s2io_club_tcp_session(u8 *buffer, u8 **tcp, u32 *tcp_len, struct lro **lro,
|
||||
struct RxD_t *rxdp, struct s2io_nic *sp)
|
||||
s2io_club_tcp_session(struct ring_info *ring_data, u8 *buffer, u8 **tcp,
|
||||
u32 *tcp_len, struct lro **lro, struct RxD_t *rxdp,
|
||||
struct s2io_nic *sp)
|
||||
{
|
||||
struct iphdr *ip;
|
||||
struct tcphdr *tcph;
|
||||
@ -8410,7 +8421,7 @@ s2io_club_tcp_session(u8 *buffer, u8 **tcp, u32 *tcp_len, struct lro **lro,
|
||||
tcph = (struct tcphdr *)*tcp;
|
||||
*tcp_len = get_l4_pyld_length(ip, tcph);
|
||||
for (i=0; i<MAX_LRO_SESSIONS; i++) {
|
||||
struct lro *l_lro = &sp->lro0_n[i];
|
||||
struct lro *l_lro = &ring_data->lro0_n[i];
|
||||
if (l_lro->in_use) {
|
||||
if (check_for_socket_match(l_lro, ip, tcph))
|
||||
continue;
|
||||
@ -8448,7 +8459,7 @@ s2io_club_tcp_session(u8 *buffer, u8 **tcp, u32 *tcp_len, struct lro **lro,
|
||||
}
|
||||
|
||||
for (i=0; i<MAX_LRO_SESSIONS; i++) {
|
||||
struct lro *l_lro = &sp->lro0_n[i];
|
||||
struct lro *l_lro = &ring_data->lro0_n[i];
|
||||
if (!(l_lro->in_use)) {
|
||||
*lro = l_lro;
|
||||
ret = 3; /* Begin anew */
|
||||
|
@ -678,11 +678,53 @@ struct rx_block_info {
|
||||
struct rxd_info *rxds;
|
||||
};
|
||||
|
||||
/* Data structure to represent a LRO session */
|
||||
struct lro {
|
||||
struct sk_buff *parent;
|
||||
struct sk_buff *last_frag;
|
||||
u8 *l2h;
|
||||
struct iphdr *iph;
|
||||
struct tcphdr *tcph;
|
||||
u32 tcp_next_seq;
|
||||
__be32 tcp_ack;
|
||||
int total_len;
|
||||
int frags_len;
|
||||
int sg_num;
|
||||
int in_use;
|
||||
__be16 window;
|
||||
u16 vlan_tag;
|
||||
u32 cur_tsval;
|
||||
__be32 cur_tsecr;
|
||||
u8 saw_ts;
|
||||
} ____cacheline_aligned;
|
||||
|
||||
/* Ring specific structure */
|
||||
struct ring_info {
|
||||
/* The ring number */
|
||||
int ring_no;
|
||||
|
||||
/* per-ring buffer counter */
|
||||
u32 rx_bufs_left;
|
||||
|
||||
#define MAX_LRO_SESSIONS 32
|
||||
struct lro lro0_n[MAX_LRO_SESSIONS];
|
||||
u8 lro;
|
||||
|
||||
/* copy of sp->rxd_mode flag */
|
||||
int rxd_mode;
|
||||
|
||||
/* Number of rxds per block for the rxd_mode */
|
||||
int rxd_count;
|
||||
|
||||
/* copy of sp pointer */
|
||||
struct s2io_nic *nic;
|
||||
|
||||
/* copy of sp->dev pointer */
|
||||
struct net_device *dev;
|
||||
|
||||
/* copy of sp->pdev pointer */
|
||||
struct pci_dev *pdev;
|
||||
|
||||
/*
|
||||
* Place holders for the virtual and physical addresses of
|
||||
* all the Rx Blocks
|
||||
@ -703,10 +745,16 @@ struct ring_info {
|
||||
*/
|
||||
struct rx_curr_get_info rx_curr_get_info;
|
||||
|
||||
/* interface MTU value */
|
||||
unsigned mtu;
|
||||
|
||||
/* Buffer Address store. */
|
||||
struct buffAdd **ba;
|
||||
struct s2io_nic *nic;
|
||||
};
|
||||
|
||||
/* per-Ring statistics */
|
||||
unsigned long rx_packets;
|
||||
unsigned long rx_bytes;
|
||||
} ____cacheline_aligned;
|
||||
|
||||
/* Fifo specific structure */
|
||||
struct fifo_info {
|
||||
@ -813,26 +861,6 @@ struct msix_info_st {
|
||||
u64 data;
|
||||
};
|
||||
|
||||
/* Data structure to represent a LRO session */
|
||||
struct lro {
|
||||
struct sk_buff *parent;
|
||||
struct sk_buff *last_frag;
|
||||
u8 *l2h;
|
||||
struct iphdr *iph;
|
||||
struct tcphdr *tcph;
|
||||
u32 tcp_next_seq;
|
||||
__be32 tcp_ack;
|
||||
int total_len;
|
||||
int frags_len;
|
||||
int sg_num;
|
||||
int in_use;
|
||||
__be16 window;
|
||||
u16 vlan_tag;
|
||||
u32 cur_tsval;
|
||||
__be32 cur_tsecr;
|
||||
u8 saw_ts;
|
||||
} ____cacheline_aligned;
|
||||
|
||||
/* These flags represent the devices temporary state */
|
||||
enum s2io_device_state_t
|
||||
{
|
||||
@ -872,8 +900,6 @@ struct s2io_nic {
|
||||
/* Space to back up the PCI config space */
|
||||
u32 config_space[256 / sizeof(u32)];
|
||||
|
||||
atomic_t rx_bufs_left[MAX_RX_RINGS];
|
||||
|
||||
#define PROMISC 1
|
||||
#define ALL_MULTI 2
|
||||
|
||||
@ -950,8 +976,6 @@ struct s2io_nic {
|
||||
#define XFRAME_II_DEVICE 2
|
||||
u8 device_type;
|
||||
|
||||
#define MAX_LRO_SESSIONS 32
|
||||
struct lro lro0_n[MAX_LRO_SESSIONS];
|
||||
unsigned long clubbed_frms_cnt;
|
||||
unsigned long sending_both;
|
||||
u8 lro;
|
||||
@ -1118,9 +1142,9 @@ static int do_s2io_add_mc(struct s2io_nic *sp, u8 *addr);
|
||||
static int do_s2io_add_mac(struct s2io_nic *sp, u64 addr, int offset);
|
||||
static int do_s2io_delete_unicast_mc(struct s2io_nic *sp, u64 addr);
|
||||
|
||||
static int
|
||||
s2io_club_tcp_session(u8 *buffer, u8 **tcp, u32 *tcp_len, struct lro **lro,
|
||||
struct RxD_t *rxdp, struct s2io_nic *sp);
|
||||
static int s2io_club_tcp_session(struct ring_info *ring_data, u8 *buffer,
|
||||
u8 **tcp, u32 *tcp_len, struct lro **lro, struct RxD_t *rxdp,
|
||||
struct s2io_nic *sp);
|
||||
static void clear_lro_session(struct lro *lro);
|
||||
static void queue_rx_frame(struct sk_buff *skb, u16 vlan_tag);
|
||||
static void update_L3L4_header(struct s2io_nic *sp, struct lro *lro);
|
||||
|
12
drivers/net/sfc/Kconfig
Normal file
12
drivers/net/sfc/Kconfig
Normal file
@ -0,0 +1,12 @@
|
||||
config SFC
|
||||
tristate "Solarflare Solarstorm SFC4000 support"
|
||||
depends on PCI && INET
|
||||
select MII
|
||||
select INET_LRO
|
||||
select CRC32
|
||||
help
|
||||
This driver supports 10-gigabit Ethernet cards based on
|
||||
the Solarflare Communications Solarstorm SFC4000 controller.
|
||||
|
||||
To compile this driver as a module, choose M here. The module
|
||||
will be called sfc.
|
5
drivers/net/sfc/Makefile
Normal file
5
drivers/net/sfc/Makefile
Normal file
@ -0,0 +1,5 @@
|
||||
sfc-y += efx.o falcon.o tx.o rx.o falcon_xmac.o \
|
||||
i2c-direct.o ethtool.o xfp_phy.o mdio_10g.o \
|
||||
tenxpress.o boards.o sfe4001.o
|
||||
|
||||
obj-$(CONFIG_SFC) += sfc.o
|
508
drivers/net/sfc/bitfield.h
Normal file
508
drivers/net/sfc/bitfield.h
Normal file
@ -0,0 +1,508 @@
|
||||
/****************************************************************************
|
||||
* Driver for Solarflare Solarstorm network controllers and boards
|
||||
* Copyright 2005-2006 Fen Systems Ltd.
|
||||
* Copyright 2006-2008 Solarflare Communications Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published
|
||||
* by the Free Software Foundation, incorporated herein by reference.
|
||||
*/
|
||||
|
||||
#ifndef EFX_BITFIELD_H
|
||||
#define EFX_BITFIELD_H
|
||||
|
||||
/*
|
||||
* Efx bitfield access
|
||||
*
|
||||
* Efx NICs make extensive use of bitfields up to 128 bits
|
||||
* wide. Since there is no native 128-bit datatype on most systems,
|
||||
* and since 64-bit datatypes are inefficient on 32-bit systems and
|
||||
* vice versa, we wrap accesses in a way that uses the most efficient
|
||||
* datatype.
|
||||
*
|
||||
* The NICs are PCI devices and therefore little-endian. Since most
|
||||
* of the quantities that we deal with are DMAed to/from host memory,
|
||||
* we define our datatypes (efx_oword_t, efx_qword_t and
|
||||
* efx_dword_t) to be little-endian.
|
||||
*/
|
||||
|
||||
/* Lowest bit numbers and widths */
|
||||
#define EFX_DUMMY_FIELD_LBN 0
|
||||
#define EFX_DUMMY_FIELD_WIDTH 0
|
||||
#define EFX_DWORD_0_LBN 0
|
||||
#define EFX_DWORD_0_WIDTH 32
|
||||
#define EFX_DWORD_1_LBN 32
|
||||
#define EFX_DWORD_1_WIDTH 32
|
||||
#define EFX_DWORD_2_LBN 64
|
||||
#define EFX_DWORD_2_WIDTH 32
|
||||
#define EFX_DWORD_3_LBN 96
|
||||
#define EFX_DWORD_3_WIDTH 32
|
||||
|
||||
/* Specified attribute (e.g. LBN) of the specified field */
|
||||
#define EFX_VAL(field, attribute) field ## _ ## attribute
|
||||
/* Low bit number of the specified field */
|
||||
#define EFX_LOW_BIT(field) EFX_VAL(field, LBN)
|
||||
/* Bit width of the specified field */
|
||||
#define EFX_WIDTH(field) EFX_VAL(field, WIDTH)
|
||||
/* High bit number of the specified field */
|
||||
#define EFX_HIGH_BIT(field) (EFX_LOW_BIT(field) + EFX_WIDTH(field) - 1)
|
||||
/* Mask equal in width to the specified field.
|
||||
*
|
||||
* For example, a field with width 5 would have a mask of 0x1f.
|
||||
*
|
||||
* The maximum width mask that can be generated is 64 bits.
|
||||
*/
|
||||
#define EFX_MASK64(field) \
|
||||
(EFX_WIDTH(field) == 64 ? ~((u64) 0) : \
|
||||
(((((u64) 1) << EFX_WIDTH(field))) - 1))
|
||||
|
||||
/* Mask equal in width to the specified field.
|
||||
*
|
||||
* For example, a field with width 5 would have a mask of 0x1f.
|
||||
*
|
||||
* The maximum width mask that can be generated is 32 bits. Use
|
||||
* EFX_MASK64 for higher width fields.
|
||||
*/
|
||||
#define EFX_MASK32(field) \
|
||||
(EFX_WIDTH(field) == 32 ? ~((u32) 0) : \
|
||||
(((((u32) 1) << EFX_WIDTH(field))) - 1))
|
||||
|
||||
/* A doubleword (i.e. 4 byte) datatype - little-endian in HW */
|
||||
typedef union efx_dword {
|
||||
__le32 u32[1];
|
||||
} efx_dword_t;
|
||||
|
||||
/* A quadword (i.e. 8 byte) datatype - little-endian in HW */
|
||||
typedef union efx_qword {
|
||||
__le64 u64[1];
|
||||
__le32 u32[2];
|
||||
efx_dword_t dword[2];
|
||||
} efx_qword_t;
|
||||
|
||||
/* An octword (eight-word, i.e. 16 byte) datatype - little-endian in HW */
|
||||
typedef union efx_oword {
|
||||
__le64 u64[2];
|
||||
efx_qword_t qword[2];
|
||||
__le32 u32[4];
|
||||
efx_dword_t dword[4];
|
||||
} efx_oword_t;
|
||||
|
||||
/* Format string and value expanders for printk */
|
||||
#define EFX_DWORD_FMT "%08x"
|
||||
#define EFX_QWORD_FMT "%08x:%08x"
|
||||
#define EFX_OWORD_FMT "%08x:%08x:%08x:%08x"
|
||||
#define EFX_DWORD_VAL(dword) \
|
||||
((unsigned int) le32_to_cpu((dword).u32[0]))
|
||||
#define EFX_QWORD_VAL(qword) \
|
||||
((unsigned int) le32_to_cpu((qword).u32[1])), \
|
||||
((unsigned int) le32_to_cpu((qword).u32[0]))
|
||||
#define EFX_OWORD_VAL(oword) \
|
||||
((unsigned int) le32_to_cpu((oword).u32[3])), \
|
||||
((unsigned int) le32_to_cpu((oword).u32[2])), \
|
||||
((unsigned int) le32_to_cpu((oword).u32[1])), \
|
||||
((unsigned int) le32_to_cpu((oword).u32[0]))
|
||||
|
||||
/*
|
||||
* Extract bit field portion [low,high) from the native-endian element
|
||||
* which contains bits [min,max).
|
||||
*
|
||||
* For example, suppose "element" represents the high 32 bits of a
|
||||
* 64-bit value, and we wish to extract the bits belonging to the bit
|
||||
* field occupying bits 28-45 of this 64-bit value.
|
||||
*
|
||||
* Then EFX_EXTRACT ( element, 32, 63, 28, 45 ) would give
|
||||
*
|
||||
* ( element ) << 4
|
||||
*
|
||||
* The result will contain the relevant bits filled in in the range
|
||||
* [0,high-low), with garbage in bits [high-low+1,...).
|
||||
*/
|
||||
#define EFX_EXTRACT_NATIVE(native_element, min, max, low, high) \
|
||||
(((low > max) || (high < min)) ? 0 : \
|
||||
((low > min) ? \
|
||||
((native_element) >> (low - min)) : \
|
||||
((native_element) << (min - low))))
|
||||
|
||||
/*
|
||||
* Extract bit field portion [low,high) from the 64-bit little-endian
|
||||
* element which contains bits [min,max)
|
||||
*/
|
||||
#define EFX_EXTRACT64(element, min, max, low, high) \
|
||||
EFX_EXTRACT_NATIVE(le64_to_cpu(element), min, max, low, high)
|
||||
|
||||
/*
|
||||
* Extract bit field portion [low,high) from the 32-bit little-endian
|
||||
* element which contains bits [min,max)
|
||||
*/
|
||||
#define EFX_EXTRACT32(element, min, max, low, high) \
|
||||
EFX_EXTRACT_NATIVE(le32_to_cpu(element), min, max, low, high)
|
||||
|
||||
#define EFX_EXTRACT_OWORD64(oword, low, high) \
|
||||
(EFX_EXTRACT64((oword).u64[0], 0, 63, low, high) | \
|
||||
EFX_EXTRACT64((oword).u64[1], 64, 127, low, high))
|
||||
|
||||
#define EFX_EXTRACT_QWORD64(qword, low, high) \
|
||||
EFX_EXTRACT64((qword).u64[0], 0, 63, low, high)
|
||||
|
||||
#define EFX_EXTRACT_OWORD32(oword, low, high) \
|
||||
(EFX_EXTRACT32((oword).u32[0], 0, 31, low, high) | \
|
||||
EFX_EXTRACT32((oword).u32[1], 32, 63, low, high) | \
|
||||
EFX_EXTRACT32((oword).u32[2], 64, 95, low, high) | \
|
||||
EFX_EXTRACT32((oword).u32[3], 96, 127, low, high))
|
||||
|
||||
#define EFX_EXTRACT_QWORD32(qword, low, high) \
|
||||
(EFX_EXTRACT32((qword).u32[0], 0, 31, low, high) | \
|
||||
EFX_EXTRACT32((qword).u32[1], 32, 63, low, high))
|
||||
|
||||
#define EFX_EXTRACT_DWORD(dword, low, high) \
|
||||
EFX_EXTRACT32((dword).u32[0], 0, 31, low, high)
|
||||
|
||||
#define EFX_OWORD_FIELD64(oword, field) \
|
||||
(EFX_EXTRACT_OWORD64(oword, EFX_LOW_BIT(field), EFX_HIGH_BIT(field)) \
|
||||
& EFX_MASK64(field))
|
||||
|
||||
#define EFX_QWORD_FIELD64(qword, field) \
|
||||
(EFX_EXTRACT_QWORD64(qword, EFX_LOW_BIT(field), EFX_HIGH_BIT(field)) \
|
||||
& EFX_MASK64(field))
|
||||
|
||||
#define EFX_OWORD_FIELD32(oword, field) \
|
||||
(EFX_EXTRACT_OWORD32(oword, EFX_LOW_BIT(field), EFX_HIGH_BIT(field)) \
|
||||
& EFX_MASK32(field))
|
||||
|
||||
#define EFX_QWORD_FIELD32(qword, field) \
|
||||
(EFX_EXTRACT_QWORD32(qword, EFX_LOW_BIT(field), EFX_HIGH_BIT(field)) \
|
||||
& EFX_MASK32(field))
|
||||
|
||||
#define EFX_DWORD_FIELD(dword, field) \
|
||||
(EFX_EXTRACT_DWORD(dword, EFX_LOW_BIT(field), EFX_HIGH_BIT(field)) \
|
||||
& EFX_MASK32(field))
|
||||
|
||||
#define EFX_OWORD_IS_ZERO64(oword) \
|
||||
(((oword).u64[0] | (oword).u64[1]) == (__force __le64) 0)
|
||||
|
||||
#define EFX_QWORD_IS_ZERO64(qword) \
|
||||
(((qword).u64[0]) == (__force __le64) 0)
|
||||
|
||||
#define EFX_OWORD_IS_ZERO32(oword) \
|
||||
(((oword).u32[0] | (oword).u32[1] | (oword).u32[2] | (oword).u32[3]) \
|
||||
== (__force __le32) 0)
|
||||
|
||||
#define EFX_QWORD_IS_ZERO32(qword) \
|
||||
(((qword).u32[0] | (qword).u32[1]) == (__force __le32) 0)
|
||||
|
||||
#define EFX_DWORD_IS_ZERO(dword) \
|
||||
(((dword).u32[0]) == (__force __le32) 0)
|
||||
|
||||
#define EFX_OWORD_IS_ALL_ONES64(oword) \
|
||||
(((oword).u64[0] & (oword).u64[1]) == ~((__force __le64) 0))
|
||||
|
||||
#define EFX_QWORD_IS_ALL_ONES64(qword) \
|
||||
((qword).u64[0] == ~((__force __le64) 0))
|
||||
|
||||
#define EFX_OWORD_IS_ALL_ONES32(oword) \
|
||||
(((oword).u32[0] & (oword).u32[1] & (oword).u32[2] & (oword).u32[3]) \
|
||||
== ~((__force __le32) 0))
|
||||
|
||||
#define EFX_QWORD_IS_ALL_ONES32(qword) \
|
||||
(((qword).u32[0] & (qword).u32[1]) == ~((__force __le32) 0))
|
||||
|
||||
#define EFX_DWORD_IS_ALL_ONES(dword) \
|
||||
((dword).u32[0] == ~((__force __le32) 0))
|
||||
|
||||
#if BITS_PER_LONG == 64
|
||||
#define EFX_OWORD_FIELD EFX_OWORD_FIELD64
|
||||
#define EFX_QWORD_FIELD EFX_QWORD_FIELD64
|
||||
#define EFX_OWORD_IS_ZERO EFX_OWORD_IS_ZERO64
|
||||
#define EFX_QWORD_IS_ZERO EFX_QWORD_IS_ZERO64
|
||||
#define EFX_OWORD_IS_ALL_ONES EFX_OWORD_IS_ALL_ONES64
|
||||
#define EFX_QWORD_IS_ALL_ONES EFX_QWORD_IS_ALL_ONES64
|
||||
#else
|
||||
#define EFX_OWORD_FIELD EFX_OWORD_FIELD32
|
||||
#define EFX_QWORD_FIELD EFX_QWORD_FIELD32
|
||||
#define EFX_OWORD_IS_ZERO EFX_OWORD_IS_ZERO32
|
||||
#define EFX_QWORD_IS_ZERO EFX_QWORD_IS_ZERO32
|
||||
#define EFX_OWORD_IS_ALL_ONES EFX_OWORD_IS_ALL_ONES32
|
||||
#define EFX_QWORD_IS_ALL_ONES EFX_QWORD_IS_ALL_ONES32
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Construct bit field portion
|
||||
*
|
||||
* Creates the portion of the bit field [low,high) that lies within
|
||||
* the range [min,max).
|
||||
*/
|
||||
#define EFX_INSERT_NATIVE64(min, max, low, high, value) \
|
||||
(((low > max) || (high < min)) ? 0 : \
|
||||
((low > min) ? \
|
||||
(((u64) (value)) << (low - min)) : \
|
||||
(((u64) (value)) >> (min - low))))
|
||||
|
||||
#define EFX_INSERT_NATIVE32(min, max, low, high, value) \
|
||||
(((low > max) || (high < min)) ? 0 : \
|
||||
((low > min) ? \
|
||||
(((u32) (value)) << (low - min)) : \
|
||||
(((u32) (value)) >> (min - low))))
|
||||
|
||||
#define EFX_INSERT_NATIVE(min, max, low, high, value) \
|
||||
((((max - min) >= 32) || ((high - low) >= 32)) ? \
|
||||
EFX_INSERT_NATIVE64(min, max, low, high, value) : \
|
||||
EFX_INSERT_NATIVE32(min, max, low, high, value))
|
||||
|
||||
/*
|
||||
* Construct bit field portion
|
||||
*
|
||||
* Creates the portion of the named bit field that lies within the
|
||||
* range [min,max).
|
||||
*/
|
||||
#define EFX_INSERT_FIELD_NATIVE(min, max, field, value) \
|
||||
EFX_INSERT_NATIVE(min, max, EFX_LOW_BIT(field), \
|
||||
EFX_HIGH_BIT(field), value)
|
||||
|
||||
/*
|
||||
* Construct bit field
|
||||
*
|
||||
* Creates the portion of the named bit fields that lie within the
|
||||
* range [min,max).
|
||||
*/
|
||||
#define EFX_INSERT_FIELDS_NATIVE(min, max, \
|
||||
field1, value1, \
|
||||
field2, value2, \
|
||||
field3, value3, \
|
||||
field4, value4, \
|
||||
field5, value5, \
|
||||
field6, value6, \
|
||||
field7, value7, \
|
||||
field8, value8, \
|
||||
field9, value9, \
|
||||
field10, value10) \
|
||||
(EFX_INSERT_FIELD_NATIVE((min), (max), field1, (value1)) | \
|
||||
EFX_INSERT_FIELD_NATIVE((min), (max), field2, (value2)) | \
|
||||
EFX_INSERT_FIELD_NATIVE((min), (max), field3, (value3)) | \
|
||||
EFX_INSERT_FIELD_NATIVE((min), (max), field4, (value4)) | \
|
||||
EFX_INSERT_FIELD_NATIVE((min), (max), field5, (value5)) | \
|
||||
EFX_INSERT_FIELD_NATIVE((min), (max), field6, (value6)) | \
|
||||
EFX_INSERT_FIELD_NATIVE((min), (max), field7, (value7)) | \
|
||||
EFX_INSERT_FIELD_NATIVE((min), (max), field8, (value8)) | \
|
||||
EFX_INSERT_FIELD_NATIVE((min), (max), field9, (value9)) | \
|
||||
EFX_INSERT_FIELD_NATIVE((min), (max), field10, (value10)))
|
||||
|
||||
#define EFX_INSERT_FIELDS64(...) \
|
||||
cpu_to_le64(EFX_INSERT_FIELDS_NATIVE(__VA_ARGS__))
|
||||
|
||||
#define EFX_INSERT_FIELDS32(...) \
|
||||
cpu_to_le32(EFX_INSERT_FIELDS_NATIVE(__VA_ARGS__))
|
||||
|
||||
#define EFX_POPULATE_OWORD64(oword, ...) do { \
|
||||
(oword).u64[0] = EFX_INSERT_FIELDS64(0, 63, __VA_ARGS__); \
|
||||
(oword).u64[1] = EFX_INSERT_FIELDS64(64, 127, __VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
#define EFX_POPULATE_QWORD64(qword, ...) do { \
|
||||
(qword).u64[0] = EFX_INSERT_FIELDS64(0, 63, __VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
#define EFX_POPULATE_OWORD32(oword, ...) do { \
|
||||
(oword).u32[0] = EFX_INSERT_FIELDS32(0, 31, __VA_ARGS__); \
|
||||
(oword).u32[1] = EFX_INSERT_FIELDS32(32, 63, __VA_ARGS__); \
|
||||
(oword).u32[2] = EFX_INSERT_FIELDS32(64, 95, __VA_ARGS__); \
|
||||
(oword).u32[3] = EFX_INSERT_FIELDS32(96, 127, __VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
#define EFX_POPULATE_QWORD32(qword, ...) do { \
|
||||
(qword).u32[0] = EFX_INSERT_FIELDS32(0, 31, __VA_ARGS__); \
|
||||
(qword).u32[1] = EFX_INSERT_FIELDS32(32, 63, __VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
#define EFX_POPULATE_DWORD(dword, ...) do { \
|
||||
(dword).u32[0] = EFX_INSERT_FIELDS32(0, 31, __VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
#if BITS_PER_LONG == 64
|
||||
#define EFX_POPULATE_OWORD EFX_POPULATE_OWORD64
|
||||
#define EFX_POPULATE_QWORD EFX_POPULATE_QWORD64
|
||||
#else
|
||||
#define EFX_POPULATE_OWORD EFX_POPULATE_OWORD32
|
||||
#define EFX_POPULATE_QWORD EFX_POPULATE_QWORD32
|
||||
#endif
|
||||
|
||||
/* Populate an octword field with various numbers of arguments */
|
||||
#define EFX_POPULATE_OWORD_10 EFX_POPULATE_OWORD
|
||||
#define EFX_POPULATE_OWORD_9(oword, ...) \
|
||||
EFX_POPULATE_OWORD_10(oword, EFX_DUMMY_FIELD, 0, __VA_ARGS__)
|
||||
#define EFX_POPULATE_OWORD_8(oword, ...) \
|
||||
EFX_POPULATE_OWORD_9(oword, EFX_DUMMY_FIELD, 0, __VA_ARGS__)
|
||||
#define EFX_POPULATE_OWORD_7(oword, ...) \
|
||||
EFX_POPULATE_OWORD_8(oword, EFX_DUMMY_FIELD, 0, __VA_ARGS__)
|
||||
#define EFX_POPULATE_OWORD_6(oword, ...) \
|
||||
EFX_POPULATE_OWORD_7(oword, EFX_DUMMY_FIELD, 0, __VA_ARGS__)
|
||||
#define EFX_POPULATE_OWORD_5(oword, ...) \
|
||||
EFX_POPULATE_OWORD_6(oword, EFX_DUMMY_FIELD, 0, __VA_ARGS__)
|
||||
#define EFX_POPULATE_OWORD_4(oword, ...) \
|
||||
EFX_POPULATE_OWORD_5(oword, EFX_DUMMY_FIELD, 0, __VA_ARGS__)
|
||||
#define EFX_POPULATE_OWORD_3(oword, ...) \
|
||||
EFX_POPULATE_OWORD_4(oword, EFX_DUMMY_FIELD, 0, __VA_ARGS__)
|
||||
#define EFX_POPULATE_OWORD_2(oword, ...) \
|
||||
EFX_POPULATE_OWORD_3(oword, EFX_DUMMY_FIELD, 0, __VA_ARGS__)
|
||||
#define EFX_POPULATE_OWORD_1(oword, ...) \
|
||||
EFX_POPULATE_OWORD_2(oword, EFX_DUMMY_FIELD, 0, __VA_ARGS__)
|
||||
#define EFX_ZERO_OWORD(oword) \
|
||||
EFX_POPULATE_OWORD_1(oword, EFX_DUMMY_FIELD, 0)
|
||||
#define EFX_SET_OWORD(oword) \
|
||||
EFX_POPULATE_OWORD_4(oword, \
|
||||
EFX_DWORD_0, 0xffffffff, \
|
||||
EFX_DWORD_1, 0xffffffff, \
|
||||
EFX_DWORD_2, 0xffffffff, \
|
||||
EFX_DWORD_3, 0xffffffff)
|
||||
|
||||
/* Populate a quadword field with various numbers of arguments */
|
||||
#define EFX_POPULATE_QWORD_10 EFX_POPULATE_QWORD
|
||||
#define EFX_POPULATE_QWORD_9(qword, ...) \
|
||||
EFX_POPULATE_QWORD_10(qword, EFX_DUMMY_FIELD, 0, __VA_ARGS__)
|
||||
#define EFX_POPULATE_QWORD_8(qword, ...) \
|
||||
EFX_POPULATE_QWORD_9(qword, EFX_DUMMY_FIELD, 0, __VA_ARGS__)
|
||||
#define EFX_POPULATE_QWORD_7(qword, ...) \
|
||||
EFX_POPULATE_QWORD_8(qword, EFX_DUMMY_FIELD, 0, __VA_ARGS__)
|
||||
#define EFX_POPULATE_QWORD_6(qword, ...) \
|
||||
EFX_POPULATE_QWORD_7(qword, EFX_DUMMY_FIELD, 0, __VA_ARGS__)
|
||||
#define EFX_POPULATE_QWORD_5(qword, ...) \
|
||||
EFX_POPULATE_QWORD_6(qword, EFX_DUMMY_FIELD, 0, __VA_ARGS__)
|
||||
#define EFX_POPULATE_QWORD_4(qword, ...) \
|
||||
EFX_POPULATE_QWORD_5(qword, EFX_DUMMY_FIELD, 0, __VA_ARGS__)
|
||||
#define EFX_POPULATE_QWORD_3(qword, ...) \
|
||||
EFX_POPULATE_QWORD_4(qword, EFX_DUMMY_FIELD, 0, __VA_ARGS__)
|
||||
#define EFX_POPULATE_QWORD_2(qword, ...) \
|
||||
EFX_POPULATE_QWORD_3(qword, EFX_DUMMY_FIELD, 0, __VA_ARGS__)
|
||||
#define EFX_POPULATE_QWORD_1(qword, ...) \
|
||||
EFX_POPULATE_QWORD_2(qword, EFX_DUMMY_FIELD, 0, __VA_ARGS__)
|
||||
#define EFX_ZERO_QWORD(qword) \
|
||||
EFX_POPULATE_QWORD_1(qword, EFX_DUMMY_FIELD, 0)
|
||||
#define EFX_SET_QWORD(qword) \
|
||||
EFX_POPULATE_QWORD_2(qword, \
|
||||
EFX_DWORD_0, 0xffffffff, \
|
||||
EFX_DWORD_1, 0xffffffff)
|
||||
|
||||
/* Populate a dword field with various numbers of arguments */
|
||||
#define EFX_POPULATE_DWORD_10 EFX_POPULATE_DWORD
|
||||
#define EFX_POPULATE_DWORD_9(dword, ...) \
|
||||
EFX_POPULATE_DWORD_10(dword, EFX_DUMMY_FIELD, 0, __VA_ARGS__)
|
||||
#define EFX_POPULATE_DWORD_8(dword, ...) \
|
||||
EFX_POPULATE_DWORD_9(dword, EFX_DUMMY_FIELD, 0, __VA_ARGS__)
|
||||
#define EFX_POPULATE_DWORD_7(dword, ...) \
|
||||
EFX_POPULATE_DWORD_8(dword, EFX_DUMMY_FIELD, 0, __VA_ARGS__)
|
||||
#define EFX_POPULATE_DWORD_6(dword, ...) \
|
||||
EFX_POPULATE_DWORD_7(dword, EFX_DUMMY_FIELD, 0, __VA_ARGS__)
|
||||
#define EFX_POPULATE_DWORD_5(dword, ...) \
|
||||
EFX_POPULATE_DWORD_6(dword, EFX_DUMMY_FIELD, 0, __VA_ARGS__)
|
||||
#define EFX_POPULATE_DWORD_4(dword, ...) \
|
||||
EFX_POPULATE_DWORD_5(dword, EFX_DUMMY_FIELD, 0, __VA_ARGS__)
|
||||
#define EFX_POPULATE_DWORD_3(dword, ...) \
|
||||
EFX_POPULATE_DWORD_4(dword, EFX_DUMMY_FIELD, 0, __VA_ARGS__)
|
||||
#define EFX_POPULATE_DWORD_2(dword, ...) \
|
||||
EFX_POPULATE_DWORD_3(dword, EFX_DUMMY_FIELD, 0, __VA_ARGS__)
|
||||
#define EFX_POPULATE_DWORD_1(dword, ...) \
|
||||
EFX_POPULATE_DWORD_2(dword, EFX_DUMMY_FIELD, 0, __VA_ARGS__)
|
||||
#define EFX_ZERO_DWORD(dword) \
|
||||
EFX_POPULATE_DWORD_1(dword, EFX_DUMMY_FIELD, 0)
|
||||
#define EFX_SET_DWORD(dword) \
|
||||
EFX_POPULATE_DWORD_1(dword, EFX_DWORD_0, 0xffffffff)
|
||||
|
||||
/*
|
||||
* Modify a named field within an already-populated structure. Used
|
||||
* for read-modify-write operations.
|
||||
*
|
||||
*/
|
||||
|
||||
#define EFX_INVERT_OWORD(oword) do { \
|
||||
(oword).u64[0] = ~((oword).u64[0]); \
|
||||
(oword).u64[1] = ~((oword).u64[1]); \
|
||||
} while (0)
|
||||
|
||||
#define EFX_INSERT_FIELD64(...) \
|
||||
cpu_to_le64(EFX_INSERT_FIELD_NATIVE(__VA_ARGS__))
|
||||
|
||||
#define EFX_INSERT_FIELD32(...) \
|
||||
cpu_to_le32(EFX_INSERT_FIELD_NATIVE(__VA_ARGS__))
|
||||
|
||||
#define EFX_INPLACE_MASK64(min, max, field) \
|
||||
EFX_INSERT_FIELD64(min, max, field, EFX_MASK64(field))
|
||||
|
||||
#define EFX_INPLACE_MASK32(min, max, field) \
|
||||
EFX_INSERT_FIELD32(min, max, field, EFX_MASK32(field))
|
||||
|
||||
#define EFX_SET_OWORD_FIELD64(oword, field, value) do { \
|
||||
(oword).u64[0] = (((oword).u64[0] \
|
||||
& ~EFX_INPLACE_MASK64(0, 63, field)) \
|
||||
| EFX_INSERT_FIELD64(0, 63, field, value)); \
|
||||
(oword).u64[1] = (((oword).u64[1] \
|
||||
& ~EFX_INPLACE_MASK64(64, 127, field)) \
|
||||
| EFX_INSERT_FIELD64(64, 127, field, value)); \
|
||||
} while (0)
|
||||
|
||||
#define EFX_SET_QWORD_FIELD64(qword, field, value) do { \
|
||||
(qword).u64[0] = (((qword).u64[0] \
|
||||
& ~EFX_INPLACE_MASK64(0, 63, field)) \
|
||||
| EFX_INSERT_FIELD64(0, 63, field, value)); \
|
||||
} while (0)
|
||||
|
||||
#define EFX_SET_OWORD_FIELD32(oword, field, value) do { \
|
||||
(oword).u32[0] = (((oword).u32[0] \
|
||||
& ~EFX_INPLACE_MASK32(0, 31, field)) \
|
||||
| EFX_INSERT_FIELD32(0, 31, field, value)); \
|
||||
(oword).u32[1] = (((oword).u32[1] \
|
||||
& ~EFX_INPLACE_MASK32(32, 63, field)) \
|
||||
| EFX_INSERT_FIELD32(32, 63, field, value)); \
|
||||
(oword).u32[2] = (((oword).u32[2] \
|
||||
& ~EFX_INPLACE_MASK32(64, 95, field)) \
|
||||
| EFX_INSERT_FIELD32(64, 95, field, value)); \
|
||||
(oword).u32[3] = (((oword).u32[3] \
|
||||
& ~EFX_INPLACE_MASK32(96, 127, field)) \
|
||||
| EFX_INSERT_FIELD32(96, 127, field, value)); \
|
||||
} while (0)
|
||||
|
||||
#define EFX_SET_QWORD_FIELD32(qword, field, value) do { \
|
||||
(qword).u32[0] = (((qword).u32[0] \
|
||||
& ~EFX_INPLACE_MASK32(0, 31, field)) \
|
||||
| EFX_INSERT_FIELD32(0, 31, field, value)); \
|
||||
(qword).u32[1] = (((qword).u32[1] \
|
||||
& ~EFX_INPLACE_MASK32(32, 63, field)) \
|
||||
| EFX_INSERT_FIELD32(32, 63, field, value)); \
|
||||
} while (0)
|
||||
|
||||
#define EFX_SET_DWORD_FIELD(dword, field, value) do { \
|
||||
(dword).u32[0] = (((dword).u32[0] \
|
||||
& ~EFX_INPLACE_MASK32(0, 31, field)) \
|
||||
| EFX_INSERT_FIELD32(0, 31, field, value)); \
|
||||
} while (0)
|
||||
|
||||
#if BITS_PER_LONG == 64
|
||||
#define EFX_SET_OWORD_FIELD EFX_SET_OWORD_FIELD64
|
||||
#define EFX_SET_QWORD_FIELD EFX_SET_QWORD_FIELD64
|
||||
#else
|
||||
#define EFX_SET_OWORD_FIELD EFX_SET_OWORD_FIELD32
|
||||
#define EFX_SET_QWORD_FIELD EFX_SET_QWORD_FIELD32
|
||||
#endif
|
||||
|
||||
#define EFX_SET_OWORD_FIELD_VER(efx, oword, field, value) do { \
|
||||
if (FALCON_REV(efx) >= FALCON_REV_B0) { \
|
||||
EFX_SET_OWORD_FIELD((oword), field##_B0, (value)); \
|
||||
} else { \
|
||||
EFX_SET_OWORD_FIELD((oword), field##_A1, (value)); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define EFX_QWORD_FIELD_VER(efx, qword, field) \
|
||||
(FALCON_REV(efx) >= FALCON_REV_B0 ? \
|
||||
EFX_QWORD_FIELD((qword), field##_B0) : \
|
||||
EFX_QWORD_FIELD((qword), field##_A1))
|
||||
|
||||
/* Used to avoid compiler warnings about shift range exceeding width
|
||||
* of the data types when dma_addr_t is only 32 bits wide.
|
||||
*/
|
||||
#define DMA_ADDR_T_WIDTH (8 * sizeof(dma_addr_t))
|
||||
#define EFX_DMA_TYPE_WIDTH(width) \
|
||||
(((width) < DMA_ADDR_T_WIDTH) ? (width) : DMA_ADDR_T_WIDTH)
|
||||
#define EFX_DMA_MAX_MASK ((DMA_ADDR_T_WIDTH == 64) ? \
|
||||
~((u64) 0) : ~((u32) 0))
|
||||
#define EFX_DMA_MASK(mask) ((mask) & EFX_DMA_MAX_MASK)
|
||||
|
||||
#endif /* EFX_BITFIELD_H */
|
167
drivers/net/sfc/boards.c
Normal file
167
drivers/net/sfc/boards.c
Normal file
@ -0,0 +1,167 @@
|
||||
/****************************************************************************
|
||||
* Driver for Solarflare Solarstorm network controllers and boards
|
||||
* Copyright 2007 Solarflare Communications Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published
|
||||
* by the Free Software Foundation, incorporated herein by reference.
|
||||
*/
|
||||
|
||||
#include "net_driver.h"
|
||||
#include "phy.h"
|
||||
#include "boards.h"
|
||||
#include "efx.h"
|
||||
|
||||
/* Macros for unpacking the board revision */
|
||||
/* The revision info is in host byte order. */
|
||||
#define BOARD_TYPE(_rev) (_rev >> 8)
|
||||
#define BOARD_MAJOR(_rev) ((_rev >> 4) & 0xf)
|
||||
#define BOARD_MINOR(_rev) (_rev & 0xf)
|
||||
|
||||
/* Blink support. If the PHY has no auto-blink mode so we hang it off a timer */
|
||||
#define BLINK_INTERVAL (HZ/2)
|
||||
|
||||
static void blink_led_timer(unsigned long context)
|
||||
{
|
||||
struct efx_nic *efx = (struct efx_nic *)context;
|
||||
struct efx_blinker *bl = &efx->board_info.blinker;
|
||||
efx->board_info.set_fault_led(efx, bl->state);
|
||||
bl->state = !bl->state;
|
||||
if (bl->resubmit) {
|
||||
bl->timer.expires = jiffies + BLINK_INTERVAL;
|
||||
add_timer(&bl->timer);
|
||||
}
|
||||
}
|
||||
|
||||
static void board_blink(struct efx_nic *efx, int blink)
|
||||
{
|
||||
struct efx_blinker *blinker = &efx->board_info.blinker;
|
||||
|
||||
/* The rtnl mutex serialises all ethtool ioctls, so
|
||||
* nothing special needs doing here. */
|
||||
if (blink) {
|
||||
blinker->resubmit = 1;
|
||||
blinker->state = 0;
|
||||
setup_timer(&blinker->timer, blink_led_timer,
|
||||
(unsigned long)efx);
|
||||
blinker->timer.expires = jiffies + BLINK_INTERVAL;
|
||||
add_timer(&blinker->timer);
|
||||
} else {
|
||||
blinker->resubmit = 0;
|
||||
if (blinker->timer.function)
|
||||
del_timer_sync(&blinker->timer);
|
||||
efx->board_info.set_fault_led(efx, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
* Support for the SFE4002
|
||||
*
|
||||
*/
|
||||
/****************************************************************************/
|
||||
/* LED allocations. Note that on rev A0 boards the schematic and the reality
|
||||
* differ: red and green are swapped. Below is the fixed (A1) layout (there
|
||||
* are only 3 A0 boards in existence, so no real reason to make this
|
||||
* conditional).
|
||||
*/
|
||||
#define SFE4002_FAULT_LED (2) /* Red */
|
||||
#define SFE4002_RX_LED (0) /* Green */
|
||||
#define SFE4002_TX_LED (1) /* Amber */
|
||||
|
||||
static int sfe4002_init_leds(struct efx_nic *efx)
|
||||
{
|
||||
/* Set the TX and RX LEDs to reflect status and activity, and the
|
||||
* fault LED off */
|
||||
xfp_set_led(efx, SFE4002_TX_LED,
|
||||
QUAKE_LED_TXLINK | QUAKE_LED_LINK_ACTSTAT);
|
||||
xfp_set_led(efx, SFE4002_RX_LED,
|
||||
QUAKE_LED_RXLINK | QUAKE_LED_LINK_ACTSTAT);
|
||||
xfp_set_led(efx, SFE4002_FAULT_LED, QUAKE_LED_OFF);
|
||||
efx->board_info.blinker.led_num = SFE4002_FAULT_LED;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sfe4002_fault_led(struct efx_nic *efx, int state)
|
||||
{
|
||||
xfp_set_led(efx, SFE4002_FAULT_LED, state ? QUAKE_LED_ON :
|
||||
QUAKE_LED_OFF);
|
||||
}
|
||||
|
||||
static int sfe4002_init(struct efx_nic *efx)
|
||||
{
|
||||
efx->board_info.init_leds = sfe4002_init_leds;
|
||||
efx->board_info.set_fault_led = sfe4002_fault_led;
|
||||
efx->board_info.blink = board_blink;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* This will get expanded as board-specific details get moved out of the
|
||||
* PHY drivers. */
|
||||
struct efx_board_data {
|
||||
const char *ref_model;
|
||||
const char *gen_type;
|
||||
int (*init) (struct efx_nic *nic);
|
||||
};
|
||||
|
||||
static int dummy_init(struct efx_nic *nic)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct efx_board_data board_data[] = {
|
||||
[EFX_BOARD_INVALID] =
|
||||
{NULL, NULL, dummy_init},
|
||||
[EFX_BOARD_SFE4001] =
|
||||
{"SFE4001", "10GBASE-T adapter", sfe4001_poweron},
|
||||
[EFX_BOARD_SFE4002] =
|
||||
{"SFE4002", "XFP adapter", sfe4002_init},
|
||||
};
|
||||
|
||||
int efx_set_board_info(struct efx_nic *efx, u16 revision_info)
|
||||
{
|
||||
int rc = 0;
|
||||
struct efx_board_data *data;
|
||||
|
||||
if (BOARD_TYPE(revision_info) >= EFX_BOARD_MAX) {
|
||||
EFX_ERR(efx, "squashing unknown board type %d\n",
|
||||
BOARD_TYPE(revision_info));
|
||||
revision_info = 0;
|
||||
}
|
||||
|
||||
if (BOARD_TYPE(revision_info) == 0) {
|
||||
efx->board_info.major = 0;
|
||||
efx->board_info.minor = 0;
|
||||
/* For early boards that don't have revision info. there is
|
||||
* only 1 board for each PHY type, so we can work it out, with
|
||||
* the exception of the PHY-less boards. */
|
||||
switch (efx->phy_type) {
|
||||
case PHY_TYPE_10XPRESS:
|
||||
efx->board_info.type = EFX_BOARD_SFE4001;
|
||||
break;
|
||||
case PHY_TYPE_XFP:
|
||||
efx->board_info.type = EFX_BOARD_SFE4002;
|
||||
break;
|
||||
default:
|
||||
efx->board_info.type = 0;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
efx->board_info.type = BOARD_TYPE(revision_info);
|
||||
efx->board_info.major = BOARD_MAJOR(revision_info);
|
||||
efx->board_info.minor = BOARD_MINOR(revision_info);
|
||||
}
|
||||
|
||||
data = &board_data[efx->board_info.type];
|
||||
|
||||
/* Report the board model number or generic type for recognisable
|
||||
* boards. */
|
||||
if (efx->board_info.type != 0)
|
||||
EFX_INFO(efx, "board is %s rev %c%d\n",
|
||||
(efx->pci_dev->subsystem_vendor == EFX_VENDID_SFC)
|
||||
? data->ref_model : data->gen_type,
|
||||
'A' + efx->board_info.major, efx->board_info.minor);
|
||||
|
||||
efx->board_info.init = data->init;
|
||||
|
||||
return rc;
|
||||
}
|
26
drivers/net/sfc/boards.h
Normal file
26
drivers/net/sfc/boards.h
Normal file
@ -0,0 +1,26 @@
|
||||
/****************************************************************************
|
||||
* Driver for Solarflare Solarstorm network controllers and boards
|
||||
* Copyright 2007 Solarflare Communications Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published
|
||||
* by the Free Software Foundation, incorporated herein by reference.
|
||||
*/
|
||||
|
||||
#ifndef EFX_BOARDS_H
|
||||
#define EFX_BOARDS_H
|
||||
|
||||
/* Board IDs (must fit in 8 bits) */
|
||||
enum efx_board_type {
|
||||
EFX_BOARD_INVALID = 0,
|
||||
EFX_BOARD_SFE4001 = 1, /* SFE4001 (10GBASE-T) */
|
||||
EFX_BOARD_SFE4002 = 2,
|
||||
/* Insert new types before here */
|
||||
EFX_BOARD_MAX
|
||||
};
|
||||
|
||||
extern int efx_set_board_info(struct efx_nic *efx, u16 revision_info);
|
||||
extern int sfe4001_poweron(struct efx_nic *efx);
|
||||
extern void sfe4001_poweroff(struct efx_nic *efx);
|
||||
|
||||
#endif
|
2208
drivers/net/sfc/efx.c
Normal file
2208
drivers/net/sfc/efx.c
Normal file
File diff suppressed because it is too large
Load Diff
67
drivers/net/sfc/efx.h
Normal file
67
drivers/net/sfc/efx.h
Normal file
@ -0,0 +1,67 @@
|
||||
/****************************************************************************
|
||||
* Driver for Solarflare Solarstorm network controllers and boards
|
||||
* Copyright 2005-2006 Fen Systems Ltd.
|
||||
* Copyright 2006-2008 Solarflare Communications Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published
|
||||
* by the Free Software Foundation, incorporated herein by reference.
|
||||
*/
|
||||
|
||||
#ifndef EFX_EFX_H
|
||||
#define EFX_EFX_H
|
||||
|
||||
#include "net_driver.h"
|
||||
|
||||
/* PCI IDs */
|
||||
#define EFX_VENDID_SFC 0x1924
|
||||
#define FALCON_A_P_DEVID 0x0703
|
||||
#define FALCON_A_S_DEVID 0x6703
|
||||
#define FALCON_B_P_DEVID 0x0710
|
||||
|
||||
/* TX */
|
||||
extern int efx_xmit(struct efx_nic *efx,
|
||||
struct efx_tx_queue *tx_queue, struct sk_buff *skb);
|
||||
extern void efx_stop_queue(struct efx_nic *efx);
|
||||
extern void efx_wake_queue(struct efx_nic *efx);
|
||||
|
||||
/* RX */
|
||||
extern void efx_xmit_done(struct efx_tx_queue *tx_queue, unsigned int index);
|
||||
extern void efx_rx_packet(struct efx_rx_queue *rx_queue, unsigned int index,
|
||||
unsigned int len, int checksummed, int discard);
|
||||
extern void efx_schedule_slow_fill(struct efx_rx_queue *rx_queue, int delay);
|
||||
|
||||
/* Channels */
|
||||
extern void efx_process_channel_now(struct efx_channel *channel);
|
||||
extern int efx_flush_queues(struct efx_nic *efx);
|
||||
|
||||
/* Ports */
|
||||
extern void efx_reconfigure_port(struct efx_nic *efx);
|
||||
|
||||
/* Global */
|
||||
extern void efx_schedule_reset(struct efx_nic *efx, enum reset_type type);
|
||||
extern void efx_suspend(struct efx_nic *efx);
|
||||
extern void efx_resume(struct efx_nic *efx);
|
||||
extern void efx_init_irq_moderation(struct efx_nic *efx, int tx_usecs,
|
||||
int rx_usecs);
|
||||
extern int efx_request_power(struct efx_nic *efx, int mw, const char *name);
|
||||
extern void efx_hex_dump(const u8 *, unsigned int, const char *);
|
||||
|
||||
/* Dummy PHY ops for PHY drivers */
|
||||
extern int efx_port_dummy_op_int(struct efx_nic *efx);
|
||||
extern void efx_port_dummy_op_void(struct efx_nic *efx);
|
||||
extern void efx_port_dummy_op_blink(struct efx_nic *efx, int blink);
|
||||
|
||||
|
||||
extern unsigned int efx_monitor_interval;
|
||||
|
||||
static inline void efx_schedule_channel(struct efx_channel *channel)
|
||||
{
|
||||
EFX_TRACE(channel->efx, "channel %d scheduling NAPI poll on CPU%d\n",
|
||||
channel->channel, raw_smp_processor_id());
|
||||
channel->work_pending = 1;
|
||||
|
||||
netif_rx_schedule(channel->napi_dev, &channel->napi_str);
|
||||
}
|
||||
|
||||
#endif /* EFX_EFX_H */
|
50
drivers/net/sfc/enum.h
Normal file
50
drivers/net/sfc/enum.h
Normal file
@ -0,0 +1,50 @@
|
||||
/****************************************************************************
|
||||
* Driver for Solarflare Solarstorm network controllers and boards
|
||||
* Copyright 2007 Solarflare Communications Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published
|
||||
* by the Free Software Foundation, incorporated herein by reference.
|
||||
*/
|
||||
|
||||
#ifndef EFX_ENUM_H
|
||||
#define EFX_ENUM_H
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
/**
|
||||
* enum reset_type - reset types
|
||||
*
|
||||
* %RESET_TYPE_INVSIBLE, %RESET_TYPE_ALL, %RESET_TYPE_WORLD and
|
||||
* %RESET_TYPE_DISABLE specify the method/scope of the reset. The
|
||||
* other valuesspecify reasons, which efx_schedule_reset() will choose
|
||||
* a method for.
|
||||
*
|
||||
* @RESET_TYPE_INVISIBLE: don't reset the PHYs or interrupts
|
||||
* @RESET_TYPE_ALL: reset everything but PCI core blocks
|
||||
* @RESET_TYPE_WORLD: reset everything, save & restore PCI config
|
||||
* @RESET_TYPE_DISABLE: disable NIC
|
||||
* @RESET_TYPE_MONITOR: reset due to hardware monitor
|
||||
* @RESET_TYPE_INT_ERROR: reset due to internal error
|
||||
* @RESET_TYPE_RX_RECOVERY: reset to recover from RX datapath errors
|
||||
* @RESET_TYPE_RX_DESC_FETCH: pcie error during rx descriptor fetch
|
||||
* @RESET_TYPE_TX_DESC_FETCH: pcie error during tx descriptor fetch
|
||||
* @RESET_TYPE_TX_SKIP: hardware completed empty tx descriptors
|
||||
*/
|
||||
enum reset_type {
|
||||
RESET_TYPE_NONE = -1,
|
||||
RESET_TYPE_INVISIBLE = 0,
|
||||
RESET_TYPE_ALL = 1,
|
||||
RESET_TYPE_WORLD = 2,
|
||||
RESET_TYPE_DISABLE = 3,
|
||||
RESET_TYPE_MAX_METHOD,
|
||||
RESET_TYPE_MONITOR,
|
||||
RESET_TYPE_INT_ERROR,
|
||||
RESET_TYPE_RX_RECOVERY,
|
||||
RESET_TYPE_RX_DESC_FETCH,
|
||||
RESET_TYPE_TX_DESC_FETCH,
|
||||
RESET_TYPE_TX_SKIP,
|
||||
RESET_TYPE_MAX,
|
||||
};
|
||||
|
||||
#endif /* EFX_ENUM_H */
|
460
drivers/net/sfc/ethtool.c
Normal file
460
drivers/net/sfc/ethtool.c
Normal file
@ -0,0 +1,460 @@
|
||||
/****************************************************************************
|
||||
* Driver for Solarflare Solarstorm network controllers and boards
|
||||
* Copyright 2005-2006 Fen Systems Ltd.
|
||||
* Copyright 2006-2008 Solarflare Communications Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published
|
||||
* by the Free Software Foundation, incorporated herein by reference.
|
||||
*/
|
||||
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/ethtool.h>
|
||||
#include <linux/rtnetlink.h>
|
||||
#include "net_driver.h"
|
||||
#include "efx.h"
|
||||
#include "ethtool.h"
|
||||
#include "falcon.h"
|
||||
#include "gmii.h"
|
||||
#include "mac.h"
|
||||
|
||||
static int efx_ethtool_set_tx_csum(struct net_device *net_dev, u32 enable);
|
||||
|
||||
struct ethtool_string {
|
||||
char name[ETH_GSTRING_LEN];
|
||||
};
|
||||
|
||||
struct efx_ethtool_stat {
|
||||
const char *name;
|
||||
enum {
|
||||
EFX_ETHTOOL_STAT_SOURCE_mac_stats,
|
||||
EFX_ETHTOOL_STAT_SOURCE_nic,
|
||||
EFX_ETHTOOL_STAT_SOURCE_channel
|
||||
} source;
|
||||
unsigned offset;
|
||||
u64(*get_stat) (void *field); /* Reader function */
|
||||
};
|
||||
|
||||
/* Initialiser for a struct #efx_ethtool_stat with type-checking */
|
||||
#define EFX_ETHTOOL_STAT(stat_name, source_name, field, field_type, \
|
||||
get_stat_function) { \
|
||||
.name = #stat_name, \
|
||||
.source = EFX_ETHTOOL_STAT_SOURCE_##source_name, \
|
||||
.offset = ((((field_type *) 0) == \
|
||||
&((struct efx_##source_name *)0)->field) ? \
|
||||
offsetof(struct efx_##source_name, field) : \
|
||||
offsetof(struct efx_##source_name, field)), \
|
||||
.get_stat = get_stat_function, \
|
||||
}
|
||||
|
||||
static u64 efx_get_uint_stat(void *field)
|
||||
{
|
||||
return *(unsigned int *)field;
|
||||
}
|
||||
|
||||
static u64 efx_get_ulong_stat(void *field)
|
||||
{
|
||||
return *(unsigned long *)field;
|
||||
}
|
||||
|
||||
static u64 efx_get_u64_stat(void *field)
|
||||
{
|
||||
return *(u64 *) field;
|
||||
}
|
||||
|
||||
static u64 efx_get_atomic_stat(void *field)
|
||||
{
|
||||
return atomic_read((atomic_t *) field);
|
||||
}
|
||||
|
||||
#define EFX_ETHTOOL_ULONG_MAC_STAT(field) \
|
||||
EFX_ETHTOOL_STAT(field, mac_stats, field, \
|
||||
unsigned long, efx_get_ulong_stat)
|
||||
|
||||
#define EFX_ETHTOOL_U64_MAC_STAT(field) \
|
||||
EFX_ETHTOOL_STAT(field, mac_stats, field, \
|
||||
u64, efx_get_u64_stat)
|
||||
|
||||
#define EFX_ETHTOOL_UINT_NIC_STAT(name) \
|
||||
EFX_ETHTOOL_STAT(name, nic, n_##name, \
|
||||
unsigned int, efx_get_uint_stat)
|
||||
|
||||
#define EFX_ETHTOOL_ATOMIC_NIC_ERROR_STAT(field) \
|
||||
EFX_ETHTOOL_STAT(field, nic, field, \
|
||||
atomic_t, efx_get_atomic_stat)
|
||||
|
||||
#define EFX_ETHTOOL_UINT_CHANNEL_STAT(field) \
|
||||
EFX_ETHTOOL_STAT(field, channel, n_##field, \
|
||||
unsigned int, efx_get_uint_stat)
|
||||
|
||||
static struct efx_ethtool_stat efx_ethtool_stats[] = {
|
||||
EFX_ETHTOOL_U64_MAC_STAT(tx_bytes),
|
||||
EFX_ETHTOOL_U64_MAC_STAT(tx_good_bytes),
|
||||
EFX_ETHTOOL_U64_MAC_STAT(tx_bad_bytes),
|
||||
EFX_ETHTOOL_ULONG_MAC_STAT(tx_packets),
|
||||
EFX_ETHTOOL_ULONG_MAC_STAT(tx_bad),
|
||||
EFX_ETHTOOL_ULONG_MAC_STAT(tx_pause),
|
||||
EFX_ETHTOOL_ULONG_MAC_STAT(tx_control),
|
||||
EFX_ETHTOOL_ULONG_MAC_STAT(tx_unicast),
|
||||
EFX_ETHTOOL_ULONG_MAC_STAT(tx_multicast),
|
||||
EFX_ETHTOOL_ULONG_MAC_STAT(tx_broadcast),
|
||||
EFX_ETHTOOL_ULONG_MAC_STAT(tx_lt64),
|
||||
EFX_ETHTOOL_ULONG_MAC_STAT(tx_64),
|
||||
EFX_ETHTOOL_ULONG_MAC_STAT(tx_65_to_127),
|
||||
EFX_ETHTOOL_ULONG_MAC_STAT(tx_128_to_255),
|
||||
EFX_ETHTOOL_ULONG_MAC_STAT(tx_256_to_511),
|
||||
EFX_ETHTOOL_ULONG_MAC_STAT(tx_512_to_1023),
|
||||
EFX_ETHTOOL_ULONG_MAC_STAT(tx_1024_to_15xx),
|
||||
EFX_ETHTOOL_ULONG_MAC_STAT(tx_15xx_to_jumbo),
|
||||
EFX_ETHTOOL_ULONG_MAC_STAT(tx_gtjumbo),
|
||||
EFX_ETHTOOL_ULONG_MAC_STAT(tx_collision),
|
||||
EFX_ETHTOOL_ULONG_MAC_STAT(tx_single_collision),
|
||||
EFX_ETHTOOL_ULONG_MAC_STAT(tx_multiple_collision),
|
||||
EFX_ETHTOOL_ULONG_MAC_STAT(tx_excessive_collision),
|
||||
EFX_ETHTOOL_ULONG_MAC_STAT(tx_deferred),
|
||||
EFX_ETHTOOL_ULONG_MAC_STAT(tx_late_collision),
|
||||
EFX_ETHTOOL_ULONG_MAC_STAT(tx_excessive_deferred),
|
||||
EFX_ETHTOOL_ULONG_MAC_STAT(tx_non_tcpudp),
|
||||
EFX_ETHTOOL_ULONG_MAC_STAT(tx_mac_src_error),
|
||||
EFX_ETHTOOL_ULONG_MAC_STAT(tx_ip_src_error),
|
||||
EFX_ETHTOOL_U64_MAC_STAT(rx_bytes),
|
||||
EFX_ETHTOOL_U64_MAC_STAT(rx_good_bytes),
|
||||
EFX_ETHTOOL_U64_MAC_STAT(rx_bad_bytes),
|
||||
EFX_ETHTOOL_ULONG_MAC_STAT(rx_packets),
|
||||
EFX_ETHTOOL_ULONG_MAC_STAT(rx_good),
|
||||
EFX_ETHTOOL_ULONG_MAC_STAT(rx_bad),
|
||||
EFX_ETHTOOL_ULONG_MAC_STAT(rx_pause),
|
||||
EFX_ETHTOOL_ULONG_MAC_STAT(rx_control),
|
||||
EFX_ETHTOOL_ULONG_MAC_STAT(rx_unicast),
|
||||
EFX_ETHTOOL_ULONG_MAC_STAT(rx_multicast),
|
||||
EFX_ETHTOOL_ULONG_MAC_STAT(rx_broadcast),
|
||||
EFX_ETHTOOL_ULONG_MAC_STAT(rx_lt64),
|
||||
EFX_ETHTOOL_ULONG_MAC_STAT(rx_64),
|
||||
EFX_ETHTOOL_ULONG_MAC_STAT(rx_65_to_127),
|
||||
EFX_ETHTOOL_ULONG_MAC_STAT(rx_128_to_255),
|
||||
EFX_ETHTOOL_ULONG_MAC_STAT(rx_256_to_511),
|
||||
EFX_ETHTOOL_ULONG_MAC_STAT(rx_512_to_1023),
|
||||
EFX_ETHTOOL_ULONG_MAC_STAT(rx_1024_to_15xx),
|
||||
EFX_ETHTOOL_ULONG_MAC_STAT(rx_15xx_to_jumbo),
|
||||
EFX_ETHTOOL_ULONG_MAC_STAT(rx_gtjumbo),
|
||||
EFX_ETHTOOL_ULONG_MAC_STAT(rx_bad_lt64),
|
||||
EFX_ETHTOOL_ULONG_MAC_STAT(rx_bad_64_to_15xx),
|
||||
EFX_ETHTOOL_ULONG_MAC_STAT(rx_bad_15xx_to_jumbo),
|
||||
EFX_ETHTOOL_ULONG_MAC_STAT(rx_bad_gtjumbo),
|
||||
EFX_ETHTOOL_ULONG_MAC_STAT(rx_overflow),
|
||||
EFX_ETHTOOL_ULONG_MAC_STAT(rx_missed),
|
||||
EFX_ETHTOOL_ULONG_MAC_STAT(rx_false_carrier),
|
||||
EFX_ETHTOOL_ULONG_MAC_STAT(rx_symbol_error),
|
||||
EFX_ETHTOOL_ULONG_MAC_STAT(rx_align_error),
|
||||
EFX_ETHTOOL_ULONG_MAC_STAT(rx_length_error),
|
||||
EFX_ETHTOOL_ULONG_MAC_STAT(rx_internal_error),
|
||||
EFX_ETHTOOL_UINT_NIC_STAT(rx_nodesc_drop_cnt),
|
||||
EFX_ETHTOOL_ATOMIC_NIC_ERROR_STAT(rx_reset),
|
||||
EFX_ETHTOOL_UINT_CHANNEL_STAT(rx_tobe_disc),
|
||||
EFX_ETHTOOL_UINT_CHANNEL_STAT(rx_ip_hdr_chksum_err),
|
||||
EFX_ETHTOOL_UINT_CHANNEL_STAT(rx_tcp_udp_chksum_err),
|
||||
EFX_ETHTOOL_UINT_CHANNEL_STAT(rx_frm_trunc),
|
||||
};
|
||||
|
||||
/* Number of ethtool statistics */
|
||||
#define EFX_ETHTOOL_NUM_STATS ARRAY_SIZE(efx_ethtool_stats)
|
||||
|
||||
/**************************************************************************
|
||||
*
|
||||
* Ethtool operations
|
||||
*
|
||||
**************************************************************************
|
||||
*/
|
||||
|
||||
/* Identify device by flashing LEDs */
|
||||
static int efx_ethtool_phys_id(struct net_device *net_dev, u32 seconds)
|
||||
{
|
||||
struct efx_nic *efx = net_dev->priv;
|
||||
|
||||
efx->board_info.blink(efx, 1);
|
||||
schedule_timeout_interruptible(seconds * HZ);
|
||||
efx->board_info.blink(efx, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* This must be called with rtnl_lock held. */
|
||||
int efx_ethtool_get_settings(struct net_device *net_dev,
|
||||
struct ethtool_cmd *ecmd)
|
||||
{
|
||||
struct efx_nic *efx = net_dev->priv;
|
||||
int rc;
|
||||
|
||||
mutex_lock(&efx->mac_lock);
|
||||
rc = falcon_xmac_get_settings(efx, ecmd);
|
||||
mutex_unlock(&efx->mac_lock);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* This must be called with rtnl_lock held. */
|
||||
int efx_ethtool_set_settings(struct net_device *net_dev,
|
||||
struct ethtool_cmd *ecmd)
|
||||
{
|
||||
struct efx_nic *efx = net_dev->priv;
|
||||
int rc;
|
||||
|
||||
mutex_lock(&efx->mac_lock);
|
||||
rc = falcon_xmac_set_settings(efx, ecmd);
|
||||
mutex_unlock(&efx->mac_lock);
|
||||
if (!rc)
|
||||
efx_reconfigure_port(efx);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void efx_ethtool_get_drvinfo(struct net_device *net_dev,
|
||||
struct ethtool_drvinfo *info)
|
||||
{
|
||||
struct efx_nic *efx = net_dev->priv;
|
||||
|
||||
strlcpy(info->driver, EFX_DRIVER_NAME, sizeof(info->driver));
|
||||
strlcpy(info->version, EFX_DRIVER_VERSION, sizeof(info->version));
|
||||
strlcpy(info->bus_info, pci_name(efx->pci_dev), sizeof(info->bus_info));
|
||||
}
|
||||
|
||||
static int efx_ethtool_get_stats_count(struct net_device *net_dev)
|
||||
{
|
||||
return EFX_ETHTOOL_NUM_STATS;
|
||||
}
|
||||
|
||||
static void efx_ethtool_get_strings(struct net_device *net_dev,
|
||||
u32 string_set, u8 *strings)
|
||||
{
|
||||
struct ethtool_string *ethtool_strings =
|
||||
(struct ethtool_string *)strings;
|
||||
int i;
|
||||
|
||||
if (string_set == ETH_SS_STATS)
|
||||
for (i = 0; i < EFX_ETHTOOL_NUM_STATS; i++)
|
||||
strncpy(ethtool_strings[i].name,
|
||||
efx_ethtool_stats[i].name,
|
||||
sizeof(ethtool_strings[i].name));
|
||||
}
|
||||
|
||||
static void efx_ethtool_get_stats(struct net_device *net_dev,
|
||||
struct ethtool_stats *stats,
|
||||
u64 *data)
|
||||
{
|
||||
struct efx_nic *efx = net_dev->priv;
|
||||
struct efx_mac_stats *mac_stats = &efx->mac_stats;
|
||||
struct efx_ethtool_stat *stat;
|
||||
struct efx_channel *channel;
|
||||
int i;
|
||||
|
||||
EFX_BUG_ON_PARANOID(stats->n_stats != EFX_ETHTOOL_NUM_STATS);
|
||||
|
||||
/* Update MAC and NIC statistics */
|
||||
net_dev->get_stats(net_dev);
|
||||
|
||||
/* Fill detailed statistics buffer */
|
||||
for (i = 0; i < EFX_ETHTOOL_NUM_STATS; i++) {
|
||||
stat = &efx_ethtool_stats[i];
|
||||
switch (stat->source) {
|
||||
case EFX_ETHTOOL_STAT_SOURCE_mac_stats:
|
||||
data[i] = stat->get_stat((void *)mac_stats +
|
||||
stat->offset);
|
||||
break;
|
||||
case EFX_ETHTOOL_STAT_SOURCE_nic:
|
||||
data[i] = stat->get_stat((void *)efx + stat->offset);
|
||||
break;
|
||||
case EFX_ETHTOOL_STAT_SOURCE_channel:
|
||||
data[i] = 0;
|
||||
efx_for_each_channel(channel, efx)
|
||||
data[i] += stat->get_stat((void *)channel +
|
||||
stat->offset);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int efx_ethtool_set_tx_csum(struct net_device *net_dev, u32 enable)
|
||||
{
|
||||
struct efx_nic *efx = net_dev->priv;
|
||||
int rc;
|
||||
|
||||
rc = ethtool_op_set_tx_csum(net_dev, enable);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
efx_flush_queues(efx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int efx_ethtool_set_rx_csum(struct net_device *net_dev, u32 enable)
|
||||
{
|
||||
struct efx_nic *efx = net_dev->priv;
|
||||
|
||||
/* No way to stop the hardware doing the checks; we just
|
||||
* ignore the result.
|
||||
*/
|
||||
efx->rx_checksum_enabled = (enable ? 1 : 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u32 efx_ethtool_get_rx_csum(struct net_device *net_dev)
|
||||
{
|
||||
struct efx_nic *efx = net_dev->priv;
|
||||
|
||||
return efx->rx_checksum_enabled;
|
||||
}
|
||||
|
||||
/* Restart autonegotiation */
|
||||
static int efx_ethtool_nway_reset(struct net_device *net_dev)
|
||||
{
|
||||
struct efx_nic *efx = net_dev->priv;
|
||||
|
||||
return mii_nway_restart(&efx->mii);
|
||||
}
|
||||
|
||||
static u32 efx_ethtool_get_link(struct net_device *net_dev)
|
||||
{
|
||||
struct efx_nic *efx = net_dev->priv;
|
||||
|
||||
return efx->link_up;
|
||||
}
|
||||
|
||||
static int efx_ethtool_get_coalesce(struct net_device *net_dev,
|
||||
struct ethtool_coalesce *coalesce)
|
||||
{
|
||||
struct efx_nic *efx = net_dev->priv;
|
||||
struct efx_tx_queue *tx_queue;
|
||||
struct efx_rx_queue *rx_queue;
|
||||
struct efx_channel *channel;
|
||||
|
||||
memset(coalesce, 0, sizeof(*coalesce));
|
||||
|
||||
/* Find lowest IRQ moderation across all used TX queues */
|
||||
coalesce->tx_coalesce_usecs_irq = ~((u32) 0);
|
||||
efx_for_each_tx_queue(tx_queue, efx) {
|
||||
channel = tx_queue->channel;
|
||||
if (channel->irq_moderation < coalesce->tx_coalesce_usecs_irq) {
|
||||
if (channel->used_flags != EFX_USED_BY_RX_TX)
|
||||
coalesce->tx_coalesce_usecs_irq =
|
||||
channel->irq_moderation;
|
||||
else
|
||||
coalesce->tx_coalesce_usecs_irq = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Find lowest IRQ moderation across all used RX queues */
|
||||
coalesce->rx_coalesce_usecs_irq = ~((u32) 0);
|
||||
efx_for_each_rx_queue(rx_queue, efx) {
|
||||
channel = rx_queue->channel;
|
||||
if (channel->irq_moderation < coalesce->rx_coalesce_usecs_irq)
|
||||
coalesce->rx_coalesce_usecs_irq =
|
||||
channel->irq_moderation;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Set coalescing parameters
|
||||
* The difficulties occur for shared channels
|
||||
*/
|
||||
static int efx_ethtool_set_coalesce(struct net_device *net_dev,
|
||||
struct ethtool_coalesce *coalesce)
|
||||
{
|
||||
struct efx_nic *efx = net_dev->priv;
|
||||
struct efx_channel *channel;
|
||||
struct efx_tx_queue *tx_queue;
|
||||
unsigned tx_usecs, rx_usecs;
|
||||
|
||||
if (coalesce->use_adaptive_rx_coalesce ||
|
||||
coalesce->use_adaptive_tx_coalesce)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (coalesce->rx_coalesce_usecs || coalesce->tx_coalesce_usecs) {
|
||||
EFX_ERR(efx, "invalid coalescing setting. "
|
||||
"Only rx/tx_coalesce_usecs_irq are supported\n");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
rx_usecs = coalesce->rx_coalesce_usecs_irq;
|
||||
tx_usecs = coalesce->tx_coalesce_usecs_irq;
|
||||
|
||||
/* If the channel is shared only allow RX parameters to be set */
|
||||
efx_for_each_tx_queue(tx_queue, efx) {
|
||||
if ((tx_queue->channel->used_flags == EFX_USED_BY_RX_TX) &&
|
||||
tx_usecs) {
|
||||
EFX_ERR(efx, "Channel is shared. "
|
||||
"Only RX coalescing may be set\n");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
}
|
||||
|
||||
efx_init_irq_moderation(efx, tx_usecs, rx_usecs);
|
||||
|
||||
/* Reset channel to pick up new moderation value. Note that
|
||||
* this may change the value of the irq_moderation field
|
||||
* (e.g. to allow for hardware timer granularity).
|
||||
*/
|
||||
efx_for_each_channel(channel, efx)
|
||||
falcon_set_int_moderation(channel);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int efx_ethtool_set_pauseparam(struct net_device *net_dev,
|
||||
struct ethtool_pauseparam *pause)
|
||||
{
|
||||
struct efx_nic *efx = net_dev->priv;
|
||||
enum efx_fc_type flow_control = efx->flow_control;
|
||||
int rc;
|
||||
|
||||
flow_control &= ~(EFX_FC_RX | EFX_FC_TX | EFX_FC_AUTO);
|
||||
flow_control |= pause->rx_pause ? EFX_FC_RX : 0;
|
||||
flow_control |= pause->tx_pause ? EFX_FC_TX : 0;
|
||||
flow_control |= pause->autoneg ? EFX_FC_AUTO : 0;
|
||||
|
||||
/* Try to push the pause parameters */
|
||||
mutex_lock(&efx->mac_lock);
|
||||
rc = falcon_xmac_set_pause(efx, flow_control);
|
||||
mutex_unlock(&efx->mac_lock);
|
||||
|
||||
if (!rc)
|
||||
efx_reconfigure_port(efx);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void efx_ethtool_get_pauseparam(struct net_device *net_dev,
|
||||
struct ethtool_pauseparam *pause)
|
||||
{
|
||||
struct efx_nic *efx = net_dev->priv;
|
||||
|
||||
pause->rx_pause = (efx->flow_control & EFX_FC_RX) ? 1 : 0;
|
||||
pause->tx_pause = (efx->flow_control & EFX_FC_TX) ? 1 : 0;
|
||||
pause->autoneg = (efx->flow_control & EFX_FC_AUTO) ? 1 : 0;
|
||||
}
|
||||
|
||||
|
||||
struct ethtool_ops efx_ethtool_ops = {
|
||||
.get_settings = efx_ethtool_get_settings,
|
||||
.set_settings = efx_ethtool_set_settings,
|
||||
.get_drvinfo = efx_ethtool_get_drvinfo,
|
||||
.nway_reset = efx_ethtool_nway_reset,
|
||||
.get_link = efx_ethtool_get_link,
|
||||
.get_coalesce = efx_ethtool_get_coalesce,
|
||||
.set_coalesce = efx_ethtool_set_coalesce,
|
||||
.get_pauseparam = efx_ethtool_get_pauseparam,
|
||||
.set_pauseparam = efx_ethtool_set_pauseparam,
|
||||
.get_rx_csum = efx_ethtool_get_rx_csum,
|
||||
.set_rx_csum = efx_ethtool_set_rx_csum,
|
||||
.get_tx_csum = ethtool_op_get_tx_csum,
|
||||
.set_tx_csum = efx_ethtool_set_tx_csum,
|
||||
.get_sg = ethtool_op_get_sg,
|
||||
.set_sg = ethtool_op_set_sg,
|
||||
.get_flags = ethtool_op_get_flags,
|
||||
.set_flags = ethtool_op_set_flags,
|
||||
.get_strings = efx_ethtool_get_strings,
|
||||
.phys_id = efx_ethtool_phys_id,
|
||||
.get_stats_count = efx_ethtool_get_stats_count,
|
||||
.get_ethtool_stats = efx_ethtool_get_stats,
|
||||
};
|
27
drivers/net/sfc/ethtool.h
Normal file
27
drivers/net/sfc/ethtool.h
Normal file
@ -0,0 +1,27 @@
|
||||
/****************************************************************************
|
||||
* Driver for Solarflare Solarstorm network controllers and boards
|
||||
* Copyright 2005 Fen Systems Ltd.
|
||||
* Copyright 2006 Solarflare Communications Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published
|
||||
* by the Free Software Foundation, incorporated herein by reference.
|
||||
*/
|
||||
|
||||
#ifndef EFX_ETHTOOL_H
|
||||
#define EFX_ETHTOOL_H
|
||||
|
||||
#include "net_driver.h"
|
||||
|
||||
/*
|
||||
* Ethtool support
|
||||
*/
|
||||
|
||||
extern int efx_ethtool_get_settings(struct net_device *net_dev,
|
||||
struct ethtool_cmd *ecmd);
|
||||
extern int efx_ethtool_set_settings(struct net_device *net_dev,
|
||||
struct ethtool_cmd *ecmd);
|
||||
|
||||
extern struct ethtool_ops efx_ethtool_ops;
|
||||
|
||||
#endif /* EFX_ETHTOOL_H */
|
2722
drivers/net/sfc/falcon.c
Normal file
2722
drivers/net/sfc/falcon.c
Normal file
File diff suppressed because it is too large
Load Diff
130
drivers/net/sfc/falcon.h
Normal file
130
drivers/net/sfc/falcon.h
Normal file
@ -0,0 +1,130 @@
|
||||
/****************************************************************************
|
||||
* Driver for Solarflare Solarstorm network controllers and boards
|
||||
* Copyright 2005-2006 Fen Systems Ltd.
|
||||
* Copyright 2006-2008 Solarflare Communications Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published
|
||||
* by the Free Software Foundation, incorporated herein by reference.
|
||||
*/
|
||||
|
||||
#ifndef EFX_FALCON_H
|
||||
#define EFX_FALCON_H
|
||||
|
||||
#include "net_driver.h"
|
||||
|
||||
/*
|
||||
* Falcon hardware control
|
||||
*/
|
||||
|
||||
enum falcon_revision {
|
||||
FALCON_REV_A0 = 0,
|
||||
FALCON_REV_A1 = 1,
|
||||
FALCON_REV_B0 = 2,
|
||||
};
|
||||
|
||||
#define FALCON_REV(efx) ((efx)->pci_dev->revision)
|
||||
|
||||
extern struct efx_nic_type falcon_a_nic_type;
|
||||
extern struct efx_nic_type falcon_b_nic_type;
|
||||
|
||||
/**************************************************************************
|
||||
*
|
||||
* Externs
|
||||
*
|
||||
**************************************************************************
|
||||
*/
|
||||
|
||||
/* TX data path */
|
||||
extern int falcon_probe_tx(struct efx_tx_queue *tx_queue);
|
||||
extern int falcon_init_tx(struct efx_tx_queue *tx_queue);
|
||||
extern void falcon_fini_tx(struct efx_tx_queue *tx_queue);
|
||||
extern void falcon_remove_tx(struct efx_tx_queue *tx_queue);
|
||||
extern void falcon_push_buffers(struct efx_tx_queue *tx_queue);
|
||||
|
||||
/* RX data path */
|
||||
extern int falcon_probe_rx(struct efx_rx_queue *rx_queue);
|
||||
extern int falcon_init_rx(struct efx_rx_queue *rx_queue);
|
||||
extern void falcon_fini_rx(struct efx_rx_queue *rx_queue);
|
||||
extern void falcon_remove_rx(struct efx_rx_queue *rx_queue);
|
||||
extern void falcon_notify_rx_desc(struct efx_rx_queue *rx_queue);
|
||||
|
||||
/* Event data path */
|
||||
extern int falcon_probe_eventq(struct efx_channel *channel);
|
||||
extern int falcon_init_eventq(struct efx_channel *channel);
|
||||
extern void falcon_fini_eventq(struct efx_channel *channel);
|
||||
extern void falcon_remove_eventq(struct efx_channel *channel);
|
||||
extern int falcon_process_eventq(struct efx_channel *channel, int *rx_quota);
|
||||
extern void falcon_eventq_read_ack(struct efx_channel *channel);
|
||||
|
||||
/* Ports */
|
||||
extern int falcon_probe_port(struct efx_nic *efx);
|
||||
extern void falcon_remove_port(struct efx_nic *efx);
|
||||
|
||||
/* MAC/PHY */
|
||||
extern int falcon_xaui_link_ok(struct efx_nic *efx);
|
||||
extern int falcon_dma_stats(struct efx_nic *efx,
|
||||
unsigned int done_offset);
|
||||
extern void falcon_drain_tx_fifo(struct efx_nic *efx);
|
||||
extern void falcon_deconfigure_mac_wrapper(struct efx_nic *efx);
|
||||
extern void falcon_reconfigure_mac_wrapper(struct efx_nic *efx);
|
||||
|
||||
/* Interrupts and test events */
|
||||
extern int falcon_init_interrupt(struct efx_nic *efx);
|
||||
extern void falcon_enable_interrupts(struct efx_nic *efx);
|
||||
extern void falcon_generate_test_event(struct efx_channel *channel,
|
||||
unsigned int magic);
|
||||
extern void falcon_generate_interrupt(struct efx_nic *efx);
|
||||
extern void falcon_set_int_moderation(struct efx_channel *channel);
|
||||
extern void falcon_disable_interrupts(struct efx_nic *efx);
|
||||
extern void falcon_fini_interrupt(struct efx_nic *efx);
|
||||
|
||||
/* Global Resources */
|
||||
extern int falcon_probe_nic(struct efx_nic *efx);
|
||||
extern int falcon_probe_resources(struct efx_nic *efx);
|
||||
extern int falcon_init_nic(struct efx_nic *efx);
|
||||
extern int falcon_reset_hw(struct efx_nic *efx, enum reset_type method);
|
||||
extern void falcon_remove_resources(struct efx_nic *efx);
|
||||
extern void falcon_remove_nic(struct efx_nic *efx);
|
||||
extern void falcon_update_nic_stats(struct efx_nic *efx);
|
||||
extern void falcon_set_multicast_hash(struct efx_nic *efx);
|
||||
extern int falcon_reset_xaui(struct efx_nic *efx);
|
||||
|
||||
/**************************************************************************
|
||||
*
|
||||
* Falcon MAC stats
|
||||
*
|
||||
**************************************************************************
|
||||
*/
|
||||
|
||||
#define FALCON_STAT_OFFSET(falcon_stat) EFX_VAL(falcon_stat, offset)
|
||||
#define FALCON_STAT_WIDTH(falcon_stat) EFX_VAL(falcon_stat, WIDTH)
|
||||
|
||||
/* Retrieve statistic from statistics block */
|
||||
#define FALCON_STAT(efx, falcon_stat, efx_stat) do { \
|
||||
if (FALCON_STAT_WIDTH(falcon_stat) == 16) \
|
||||
(efx)->mac_stats.efx_stat += le16_to_cpu( \
|
||||
*((__force __le16 *) \
|
||||
(efx->stats_buffer.addr + \
|
||||
FALCON_STAT_OFFSET(falcon_stat)))); \
|
||||
else if (FALCON_STAT_WIDTH(falcon_stat) == 32) \
|
||||
(efx)->mac_stats.efx_stat += le32_to_cpu( \
|
||||
*((__force __le32 *) \
|
||||
(efx->stats_buffer.addr + \
|
||||
FALCON_STAT_OFFSET(falcon_stat)))); \
|
||||
else \
|
||||
(efx)->mac_stats.efx_stat += le64_to_cpu( \
|
||||
*((__force __le64 *) \
|
||||
(efx->stats_buffer.addr + \
|
||||
FALCON_STAT_OFFSET(falcon_stat)))); \
|
||||
} while (0)
|
||||
|
||||
#define FALCON_MAC_STATS_SIZE 0x100
|
||||
|
||||
#define MAC_DATA_LBN 0
|
||||
#define MAC_DATA_WIDTH 32
|
||||
|
||||
extern void falcon_generate_event(struct efx_channel *channel,
|
||||
efx_qword_t *event);
|
||||
|
||||
#endif /* EFX_FALCON_H */
|
1135
drivers/net/sfc/falcon_hwdefs.h
Normal file
1135
drivers/net/sfc/falcon_hwdefs.h
Normal file
File diff suppressed because it is too large
Load Diff
243
drivers/net/sfc/falcon_io.h
Normal file
243
drivers/net/sfc/falcon_io.h
Normal file
@ -0,0 +1,243 @@
|
||||
/****************************************************************************
|
||||
* Driver for Solarflare Solarstorm network controllers and boards
|
||||
* Copyright 2005-2006 Fen Systems Ltd.
|
||||
* Copyright 2006-2008 Solarflare Communications Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published
|
||||
* by the Free Software Foundation, incorporated herein by reference.
|
||||
*/
|
||||
|
||||
#ifndef EFX_FALCON_IO_H
|
||||
#define EFX_FALCON_IO_H
|
||||
|
||||
#include <linux/io.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include "net_driver.h"
|
||||
|
||||
/**************************************************************************
|
||||
*
|
||||
* Falcon hardware access
|
||||
*
|
||||
**************************************************************************
|
||||
*
|
||||
* Notes on locking strategy:
|
||||
*
|
||||
* Most Falcon registers require 16-byte (or 8-byte, for SRAM
|
||||
* registers) atomic writes which necessitates locking.
|
||||
* Under normal operation few writes to the Falcon BAR are made and these
|
||||
* registers (EVQ_RPTR_REG, RX_DESC_UPD_REG and TX_DESC_UPD_REG) are special
|
||||
* cased to allow 4-byte (hence lockless) accesses.
|
||||
*
|
||||
* It *is* safe to write to these 4-byte registers in the middle of an
|
||||
* access to an 8-byte or 16-byte register. We therefore use a
|
||||
* spinlock to protect accesses to the larger registers, but no locks
|
||||
* for the 4-byte registers.
|
||||
*
|
||||
* A write barrier is needed to ensure that DW3 is written after DW0/1/2
|
||||
* due to the way the 16byte registers are "collected" in the Falcon BIU
|
||||
*
|
||||
* We also lock when carrying out reads, to ensure consistency of the
|
||||
* data (made possible since the BIU reads all 128 bits into a cache).
|
||||
* Reads are very rare, so this isn't a significant performance
|
||||
* impact. (Most data transferred from NIC to host is DMAed directly
|
||||
* into host memory).
|
||||
*
|
||||
* I/O BAR access uses locks for both reads and writes (but is only provided
|
||||
* for testing purposes).
|
||||
*/
|
||||
|
||||
/* Special buffer descriptors (Falcon SRAM) */
|
||||
#define BUF_TBL_KER_A1 0x18000
|
||||
#define BUF_TBL_KER_B0 0x800000
|
||||
|
||||
|
||||
#if BITS_PER_LONG == 64
|
||||
#define FALCON_USE_QWORD_IO 1
|
||||
#endif
|
||||
|
||||
#define _falcon_writeq(efx, value, reg) \
|
||||
__raw_writeq((__force u64) (value), (efx)->membase + (reg))
|
||||
#define _falcon_writel(efx, value, reg) \
|
||||
__raw_writel((__force u32) (value), (efx)->membase + (reg))
|
||||
#define _falcon_readq(efx, reg) \
|
||||
((__force __le64) __raw_readq((efx)->membase + (reg)))
|
||||
#define _falcon_readl(efx, reg) \
|
||||
((__force __le32) __raw_readl((efx)->membase + (reg)))
|
||||
|
||||
/* Writes to a normal 16-byte Falcon register, locking as appropriate. */
|
||||
static inline void falcon_write(struct efx_nic *efx, efx_oword_t *value,
|
||||
unsigned int reg)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
EFX_REGDUMP(efx, "writing register %x with " EFX_OWORD_FMT "\n", reg,
|
||||
EFX_OWORD_VAL(*value));
|
||||
|
||||
spin_lock_irqsave(&efx->biu_lock, flags);
|
||||
#ifdef FALCON_USE_QWORD_IO
|
||||
_falcon_writeq(efx, value->u64[0], reg + 0);
|
||||
wmb();
|
||||
_falcon_writeq(efx, value->u64[1], reg + 8);
|
||||
#else
|
||||
_falcon_writel(efx, value->u32[0], reg + 0);
|
||||
_falcon_writel(efx, value->u32[1], reg + 4);
|
||||
_falcon_writel(efx, value->u32[2], reg + 8);
|
||||
wmb();
|
||||
_falcon_writel(efx, value->u32[3], reg + 12);
|
||||
#endif
|
||||
mmiowb();
|
||||
spin_unlock_irqrestore(&efx->biu_lock, flags);
|
||||
}
|
||||
|
||||
/* Writes to an 8-byte Falcon SRAM register, locking as appropriate. */
|
||||
static inline void falcon_write_sram(struct efx_nic *efx, efx_qword_t *value,
|
||||
unsigned int index)
|
||||
{
|
||||
unsigned int reg = efx->type->buf_tbl_base + (index * sizeof(*value));
|
||||
unsigned long flags;
|
||||
|
||||
EFX_REGDUMP(efx, "writing SRAM register %x with " EFX_QWORD_FMT "\n",
|
||||
reg, EFX_QWORD_VAL(*value));
|
||||
|
||||
spin_lock_irqsave(&efx->biu_lock, flags);
|
||||
#ifdef FALCON_USE_QWORD_IO
|
||||
_falcon_writeq(efx, value->u64[0], reg + 0);
|
||||
#else
|
||||
_falcon_writel(efx, value->u32[0], reg + 0);
|
||||
wmb();
|
||||
_falcon_writel(efx, value->u32[1], reg + 4);
|
||||
#endif
|
||||
mmiowb();
|
||||
spin_unlock_irqrestore(&efx->biu_lock, flags);
|
||||
}
|
||||
|
||||
/* Write dword to Falcon register that allows partial writes
|
||||
*
|
||||
* Some Falcon registers (EVQ_RPTR_REG, RX_DESC_UPD_REG and
|
||||
* TX_DESC_UPD_REG) can be written to as a single dword. This allows
|
||||
* for lockless writes.
|
||||
*/
|
||||
static inline void falcon_writel(struct efx_nic *efx, efx_dword_t *value,
|
||||
unsigned int reg)
|
||||
{
|
||||
EFX_REGDUMP(efx, "writing partial register %x with "EFX_DWORD_FMT"\n",
|
||||
reg, EFX_DWORD_VAL(*value));
|
||||
|
||||
/* No lock required */
|
||||
_falcon_writel(efx, value->u32[0], reg);
|
||||
}
|
||||
|
||||
/* Read from a Falcon register
|
||||
*
|
||||
* This reads an entire 16-byte Falcon register in one go, locking as
|
||||
* appropriate. It is essential to read the first dword first, as this
|
||||
* prompts Falcon to load the current value into the shadow register.
|
||||
*/
|
||||
static inline void falcon_read(struct efx_nic *efx, efx_oword_t *value,
|
||||
unsigned int reg)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&efx->biu_lock, flags);
|
||||
value->u32[0] = _falcon_readl(efx, reg + 0);
|
||||
rmb();
|
||||
value->u32[1] = _falcon_readl(efx, reg + 4);
|
||||
value->u32[2] = _falcon_readl(efx, reg + 8);
|
||||
value->u32[3] = _falcon_readl(efx, reg + 12);
|
||||
spin_unlock_irqrestore(&efx->biu_lock, flags);
|
||||
|
||||
EFX_REGDUMP(efx, "read from register %x, got " EFX_OWORD_FMT "\n", reg,
|
||||
EFX_OWORD_VAL(*value));
|
||||
}
|
||||
|
||||
/* This reads an 8-byte Falcon SRAM entry in one go. */
|
||||
static inline void falcon_read_sram(struct efx_nic *efx, efx_qword_t *value,
|
||||
unsigned int index)
|
||||
{
|
||||
unsigned int reg = efx->type->buf_tbl_base + (index * sizeof(*value));
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&efx->biu_lock, flags);
|
||||
#ifdef FALCON_USE_QWORD_IO
|
||||
value->u64[0] = _falcon_readq(efx, reg + 0);
|
||||
#else
|
||||
value->u32[0] = _falcon_readl(efx, reg + 0);
|
||||
rmb();
|
||||
value->u32[1] = _falcon_readl(efx, reg + 4);
|
||||
#endif
|
||||
spin_unlock_irqrestore(&efx->biu_lock, flags);
|
||||
|
||||
EFX_REGDUMP(efx, "read from SRAM register %x, got "EFX_QWORD_FMT"\n",
|
||||
reg, EFX_QWORD_VAL(*value));
|
||||
}
|
||||
|
||||
/* Read dword from Falcon register that allows partial writes (sic) */
|
||||
static inline void falcon_readl(struct efx_nic *efx, efx_dword_t *value,
|
||||
unsigned int reg)
|
||||
{
|
||||
value->u32[0] = _falcon_readl(efx, reg);
|
||||
EFX_REGDUMP(efx, "read from register %x, got "EFX_DWORD_FMT"\n",
|
||||
reg, EFX_DWORD_VAL(*value));
|
||||
}
|
||||
|
||||
/* Write to a register forming part of a table */
|
||||
static inline void falcon_write_table(struct efx_nic *efx, efx_oword_t *value,
|
||||
unsigned int reg, unsigned int index)
|
||||
{
|
||||
falcon_write(efx, value, reg + index * sizeof(efx_oword_t));
|
||||
}
|
||||
|
||||
/* Read to a register forming part of a table */
|
||||
static inline void falcon_read_table(struct efx_nic *efx, efx_oword_t *value,
|
||||
unsigned int reg, unsigned int index)
|
||||
{
|
||||
falcon_read(efx, value, reg + index * sizeof(efx_oword_t));
|
||||
}
|
||||
|
||||
/* Write to a dword register forming part of a table */
|
||||
static inline void falcon_writel_table(struct efx_nic *efx, efx_dword_t *value,
|
||||
unsigned int reg, unsigned int index)
|
||||
{
|
||||
falcon_writel(efx, value, reg + index * sizeof(efx_oword_t));
|
||||
}
|
||||
|
||||
/* Page-mapped register block size */
|
||||
#define FALCON_PAGE_BLOCK_SIZE 0x2000
|
||||
|
||||
/* Calculate offset to page-mapped register block */
|
||||
#define FALCON_PAGED_REG(page, reg) \
|
||||
((page) * FALCON_PAGE_BLOCK_SIZE + (reg))
|
||||
|
||||
/* As for falcon_write(), but for a page-mapped register. */
|
||||
static inline void falcon_write_page(struct efx_nic *efx, efx_oword_t *value,
|
||||
unsigned int reg, unsigned int page)
|
||||
{
|
||||
falcon_write(efx, value, FALCON_PAGED_REG(page, reg));
|
||||
}
|
||||
|
||||
/* As for falcon_writel(), but for a page-mapped register. */
|
||||
static inline void falcon_writel_page(struct efx_nic *efx, efx_dword_t *value,
|
||||
unsigned int reg, unsigned int page)
|
||||
{
|
||||
falcon_writel(efx, value, FALCON_PAGED_REG(page, reg));
|
||||
}
|
||||
|
||||
/* Write dword to Falcon page-mapped register with an extra lock.
|
||||
*
|
||||
* As for falcon_writel_page(), but for a register that suffers from
|
||||
* SFC bug 3181. Take out a lock so the BIU collector cannot be
|
||||
* confused. */
|
||||
static inline void falcon_writel_page_locked(struct efx_nic *efx,
|
||||
efx_dword_t *value,
|
||||
unsigned int reg,
|
||||
unsigned int page)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&efx->biu_lock, flags);
|
||||
falcon_writel(efx, value, FALCON_PAGED_REG(page, reg));
|
||||
spin_unlock_irqrestore(&efx->biu_lock, flags);
|
||||
}
|
||||
|
||||
#endif /* EFX_FALCON_IO_H */
|
585
drivers/net/sfc/falcon_xmac.c
Normal file
585
drivers/net/sfc/falcon_xmac.c
Normal file
@ -0,0 +1,585 @@
|
||||
/****************************************************************************
|
||||
* Driver for Solarflare Solarstorm network controllers and boards
|
||||
* Copyright 2005-2006 Fen Systems Ltd.
|
||||
* Copyright 2006-2008 Solarflare Communications Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published
|
||||
* by the Free Software Foundation, incorporated herein by reference.
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include "net_driver.h"
|
||||
#include "efx.h"
|
||||
#include "falcon.h"
|
||||
#include "falcon_hwdefs.h"
|
||||
#include "falcon_io.h"
|
||||
#include "mac.h"
|
||||
#include "gmii.h"
|
||||
#include "mdio_10g.h"
|
||||
#include "phy.h"
|
||||
#include "boards.h"
|
||||
#include "workarounds.h"
|
||||
|
||||
/**************************************************************************
|
||||
*
|
||||
* MAC register access
|
||||
*
|
||||
**************************************************************************/
|
||||
|
||||
/* Offset of an XMAC register within Falcon */
|
||||
#define FALCON_XMAC_REG(mac_reg) \
|
||||
(FALCON_XMAC_REGBANK + ((mac_reg) * FALCON_XMAC_REG_SIZE))
|
||||
|
||||
void falcon_xmac_writel(struct efx_nic *efx,
|
||||
efx_dword_t *value, unsigned int mac_reg)
|
||||
{
|
||||
efx_oword_t temp;
|
||||
|
||||
EFX_POPULATE_OWORD_1(temp, MAC_DATA, EFX_DWORD_FIELD(*value, MAC_DATA));
|
||||
falcon_write(efx, &temp, FALCON_XMAC_REG(mac_reg));
|
||||
}
|
||||
|
||||
void falcon_xmac_readl(struct efx_nic *efx,
|
||||
efx_dword_t *value, unsigned int mac_reg)
|
||||
{
|
||||
efx_oword_t temp;
|
||||
|
||||
falcon_read(efx, &temp, FALCON_XMAC_REG(mac_reg));
|
||||
EFX_POPULATE_DWORD_1(*value, MAC_DATA, EFX_OWORD_FIELD(temp, MAC_DATA));
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
*
|
||||
* MAC operations
|
||||
*
|
||||
*************************************************************************/
|
||||
static int falcon_reset_xmac(struct efx_nic *efx)
|
||||
{
|
||||
efx_dword_t reg;
|
||||
int count;
|
||||
|
||||
EFX_POPULATE_DWORD_1(reg, XM_CORE_RST, 1);
|
||||
falcon_xmac_writel(efx, ®, XM_GLB_CFG_REG_MAC);
|
||||
|
||||
for (count = 0; count < 10000; count++) { /* wait upto 100ms */
|
||||
falcon_xmac_readl(efx, ®, XM_GLB_CFG_REG_MAC);
|
||||
if (EFX_DWORD_FIELD(reg, XM_CORE_RST) == 0)
|
||||
return 0;
|
||||
udelay(10);
|
||||
}
|
||||
|
||||
EFX_ERR(efx, "timed out waiting for XMAC core reset\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
/* Configure the XAUI driver that is an output from Falcon */
|
||||
static void falcon_setup_xaui(struct efx_nic *efx)
|
||||
{
|
||||
efx_dword_t sdctl, txdrv;
|
||||
|
||||
/* Move the XAUI into low power, unless there is no PHY, in
|
||||
* which case the XAUI will have to drive a cable. */
|
||||
if (efx->phy_type == PHY_TYPE_NONE)
|
||||
return;
|
||||
|
||||
falcon_xmac_readl(efx, &sdctl, XX_SD_CTL_REG_MAC);
|
||||
EFX_SET_DWORD_FIELD(sdctl, XX_HIDRVD, XX_SD_CTL_DRV_DEFAULT);
|
||||
EFX_SET_DWORD_FIELD(sdctl, XX_LODRVD, XX_SD_CTL_DRV_DEFAULT);
|
||||
EFX_SET_DWORD_FIELD(sdctl, XX_HIDRVC, XX_SD_CTL_DRV_DEFAULT);
|
||||
EFX_SET_DWORD_FIELD(sdctl, XX_LODRVC, XX_SD_CTL_DRV_DEFAULT);
|
||||
EFX_SET_DWORD_FIELD(sdctl, XX_HIDRVB, XX_SD_CTL_DRV_DEFAULT);
|
||||
EFX_SET_DWORD_FIELD(sdctl, XX_LODRVB, XX_SD_CTL_DRV_DEFAULT);
|
||||
EFX_SET_DWORD_FIELD(sdctl, XX_HIDRVA, XX_SD_CTL_DRV_DEFAULT);
|
||||
EFX_SET_DWORD_FIELD(sdctl, XX_LODRVA, XX_SD_CTL_DRV_DEFAULT);
|
||||
falcon_xmac_writel(efx, &sdctl, XX_SD_CTL_REG_MAC);
|
||||
|
||||
EFX_POPULATE_DWORD_8(txdrv,
|
||||
XX_DEQD, XX_TXDRV_DEQ_DEFAULT,
|
||||
XX_DEQC, XX_TXDRV_DEQ_DEFAULT,
|
||||
XX_DEQB, XX_TXDRV_DEQ_DEFAULT,
|
||||
XX_DEQA, XX_TXDRV_DEQ_DEFAULT,
|
||||
XX_DTXD, XX_TXDRV_DTX_DEFAULT,
|
||||
XX_DTXC, XX_TXDRV_DTX_DEFAULT,
|
||||
XX_DTXB, XX_TXDRV_DTX_DEFAULT,
|
||||
XX_DTXA, XX_TXDRV_DTX_DEFAULT);
|
||||
falcon_xmac_writel(efx, &txdrv, XX_TXDRV_CTL_REG_MAC);
|
||||
}
|
||||
|
||||
static void falcon_hold_xaui_in_rst(struct efx_nic *efx)
|
||||
{
|
||||
efx_dword_t reg;
|
||||
|
||||
EFX_ZERO_DWORD(reg);
|
||||
EFX_SET_DWORD_FIELD(reg, XX_PWRDNA_EN, 1);
|
||||
EFX_SET_DWORD_FIELD(reg, XX_PWRDNB_EN, 1);
|
||||
EFX_SET_DWORD_FIELD(reg, XX_PWRDNC_EN, 1);
|
||||
EFX_SET_DWORD_FIELD(reg, XX_PWRDND_EN, 1);
|
||||
EFX_SET_DWORD_FIELD(reg, XX_RSTPLLAB_EN, 1);
|
||||
EFX_SET_DWORD_FIELD(reg, XX_RSTPLLCD_EN, 1);
|
||||
EFX_SET_DWORD_FIELD(reg, XX_RESETA_EN, 1);
|
||||
EFX_SET_DWORD_FIELD(reg, XX_RESETB_EN, 1);
|
||||
EFX_SET_DWORD_FIELD(reg, XX_RESETC_EN, 1);
|
||||
EFX_SET_DWORD_FIELD(reg, XX_RESETD_EN, 1);
|
||||
EFX_SET_DWORD_FIELD(reg, XX_RSTXGXSRX_EN, 1);
|
||||
EFX_SET_DWORD_FIELD(reg, XX_RSTXGXSTX_EN, 1);
|
||||
falcon_xmac_writel(efx, ®, XX_PWR_RST_REG_MAC);
|
||||
udelay(10);
|
||||
}
|
||||
|
||||
static int _falcon_reset_xaui_a(struct efx_nic *efx)
|
||||
{
|
||||
efx_dword_t reg;
|
||||
|
||||
falcon_hold_xaui_in_rst(efx);
|
||||
falcon_xmac_readl(efx, ®, XX_PWR_RST_REG_MAC);
|
||||
|
||||
/* Follow the RAMBUS XAUI data reset sequencing
|
||||
* Channels A and B first: power down, reset PLL, reset, clear
|
||||
*/
|
||||
EFX_SET_DWORD_FIELD(reg, XX_PWRDNA_EN, 0);
|
||||
EFX_SET_DWORD_FIELD(reg, XX_PWRDNB_EN, 0);
|
||||
falcon_xmac_writel(efx, ®, XX_PWR_RST_REG_MAC);
|
||||
udelay(10);
|
||||
|
||||
EFX_SET_DWORD_FIELD(reg, XX_RSTPLLAB_EN, 0);
|
||||
falcon_xmac_writel(efx, ®, XX_PWR_RST_REG_MAC);
|
||||
udelay(10);
|
||||
|
||||
EFX_SET_DWORD_FIELD(reg, XX_RESETA_EN, 0);
|
||||
EFX_SET_DWORD_FIELD(reg, XX_RESETB_EN, 0);
|
||||
falcon_xmac_writel(efx, ®, XX_PWR_RST_REG_MAC);
|
||||
udelay(10);
|
||||
|
||||
/* Channels C and D: power down, reset PLL, reset, clear */
|
||||
EFX_SET_DWORD_FIELD(reg, XX_PWRDNC_EN, 0);
|
||||
EFX_SET_DWORD_FIELD(reg, XX_PWRDND_EN, 0);
|
||||
falcon_xmac_writel(efx, ®, XX_PWR_RST_REG_MAC);
|
||||
udelay(10);
|
||||
|
||||
EFX_SET_DWORD_FIELD(reg, XX_RSTPLLCD_EN, 0);
|
||||
falcon_xmac_writel(efx, ®, XX_PWR_RST_REG_MAC);
|
||||
udelay(10);
|
||||
|
||||
EFX_SET_DWORD_FIELD(reg, XX_RESETC_EN, 0);
|
||||
EFX_SET_DWORD_FIELD(reg, XX_RESETD_EN, 0);
|
||||
falcon_xmac_writel(efx, ®, XX_PWR_RST_REG_MAC);
|
||||
udelay(10);
|
||||
|
||||
/* Setup XAUI */
|
||||
falcon_setup_xaui(efx);
|
||||
udelay(10);
|
||||
|
||||
/* Take XGXS out of reset */
|
||||
EFX_ZERO_DWORD(reg);
|
||||
falcon_xmac_writel(efx, ®, XX_PWR_RST_REG_MAC);
|
||||
udelay(10);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _falcon_reset_xaui_b(struct efx_nic *efx)
|
||||
{
|
||||
efx_dword_t reg;
|
||||
int count;
|
||||
|
||||
EFX_POPULATE_DWORD_1(reg, XX_RST_XX_EN, 1);
|
||||
falcon_xmac_writel(efx, ®, XX_PWR_RST_REG_MAC);
|
||||
|
||||
/* Give some time for the link to establish */
|
||||
for (count = 0; count < 1000; count++) { /* wait upto 10ms */
|
||||
falcon_xmac_readl(efx, ®, XX_PWR_RST_REG_MAC);
|
||||
if (EFX_DWORD_FIELD(reg, XX_RST_XX_EN) == 0) {
|
||||
falcon_setup_xaui(efx);
|
||||
return 0;
|
||||
}
|
||||
udelay(10);
|
||||
}
|
||||
EFX_ERR(efx, "timed out waiting for XAUI/XGXS reset\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
int falcon_reset_xaui(struct efx_nic *efx)
|
||||
{
|
||||
int rc;
|
||||
|
||||
if (EFX_WORKAROUND_9388(efx)) {
|
||||
falcon_hold_xaui_in_rst(efx);
|
||||
efx->phy_op->reset_xaui(efx);
|
||||
rc = _falcon_reset_xaui_a(efx);
|
||||
} else {
|
||||
rc = _falcon_reset_xaui_b(efx);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int falcon_xgmii_status(struct efx_nic *efx)
|
||||
{
|
||||
efx_dword_t reg;
|
||||
|
||||
if (FALCON_REV(efx) < FALCON_REV_B0)
|
||||
return 1;
|
||||
|
||||
/* The ISR latches, so clear it and re-read */
|
||||
falcon_xmac_readl(efx, ®, XM_MGT_INT_REG_MAC_B0);
|
||||
falcon_xmac_readl(efx, ®, XM_MGT_INT_REG_MAC_B0);
|
||||
|
||||
if (EFX_DWORD_FIELD(reg, XM_LCLFLT) ||
|
||||
EFX_DWORD_FIELD(reg, XM_RMTFLT)) {
|
||||
EFX_INFO(efx, "MGT_INT: "EFX_DWORD_FMT"\n", EFX_DWORD_VAL(reg));
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void falcon_mask_status_intr(struct efx_nic *efx, int enable)
|
||||
{
|
||||
efx_dword_t reg;
|
||||
|
||||
if (FALCON_REV(efx) < FALCON_REV_B0)
|
||||
return;
|
||||
|
||||
/* Flush the ISR */
|
||||
if (enable)
|
||||
falcon_xmac_readl(efx, ®, XM_MGT_INT_REG_MAC_B0);
|
||||
|
||||
EFX_POPULATE_DWORD_2(reg,
|
||||
XM_MSK_RMTFLT, !enable,
|
||||
XM_MSK_LCLFLT, !enable);
|
||||
falcon_xmac_writel(efx, ®, XM_MGT_INT_MSK_REG_MAC_B0);
|
||||
}
|
||||
|
||||
int falcon_init_xmac(struct efx_nic *efx)
|
||||
{
|
||||
int rc;
|
||||
|
||||
/* Initialize the PHY first so the clock is around */
|
||||
rc = efx->phy_op->init(efx);
|
||||
if (rc)
|
||||
goto fail1;
|
||||
|
||||
rc = falcon_reset_xaui(efx);
|
||||
if (rc)
|
||||
goto fail2;
|
||||
|
||||
/* Wait again. Give the PHY and MAC time to come back */
|
||||
schedule_timeout_uninterruptible(HZ / 10);
|
||||
|
||||
rc = falcon_reset_xmac(efx);
|
||||
if (rc)
|
||||
goto fail2;
|
||||
|
||||
falcon_mask_status_intr(efx, 1);
|
||||
return 0;
|
||||
|
||||
fail2:
|
||||
efx->phy_op->fini(efx);
|
||||
fail1:
|
||||
return rc;
|
||||
}
|
||||
|
||||
int falcon_xaui_link_ok(struct efx_nic *efx)
|
||||
{
|
||||
efx_dword_t reg;
|
||||
int align_done, sync_status, link_ok = 0;
|
||||
|
||||
/* Read link status */
|
||||
falcon_xmac_readl(efx, ®, XX_CORE_STAT_REG_MAC);
|
||||
|
||||
align_done = EFX_DWORD_FIELD(reg, XX_ALIGN_DONE);
|
||||
sync_status = EFX_DWORD_FIELD(reg, XX_SYNC_STAT);
|
||||
if (align_done && (sync_status == XX_SYNC_STAT_DECODE_SYNCED))
|
||||
link_ok = 1;
|
||||
|
||||
/* Clear link status ready for next read */
|
||||
EFX_SET_DWORD_FIELD(reg, XX_COMMA_DET, XX_COMMA_DET_RESET);
|
||||
EFX_SET_DWORD_FIELD(reg, XX_CHARERR, XX_CHARERR_RESET);
|
||||
EFX_SET_DWORD_FIELD(reg, XX_DISPERR, XX_DISPERR_RESET);
|
||||
falcon_xmac_writel(efx, ®, XX_CORE_STAT_REG_MAC);
|
||||
|
||||
/* If the link is up, then check the phy side of the xaui link
|
||||
* (error conditions from the wire side propoagate back through
|
||||
* the phy to the xaui side). */
|
||||
if (efx->link_up && link_ok) {
|
||||
int has_phyxs = efx->phy_op->mmds & (1 << MDIO_MMD_PHYXS);
|
||||
if (has_phyxs)
|
||||
link_ok = mdio_clause45_phyxgxs_lane_sync(efx);
|
||||
}
|
||||
|
||||
/* If the PHY and XAUI links are up, then check the mac's xgmii
|
||||
* fault state */
|
||||
if (efx->link_up && link_ok)
|
||||
link_ok = falcon_xgmii_status(efx);
|
||||
|
||||
return link_ok;
|
||||
}
|
||||
|
||||
static void falcon_reconfigure_xmac_core(struct efx_nic *efx)
|
||||
{
|
||||
unsigned int max_frame_len;
|
||||
efx_dword_t reg;
|
||||
int rx_fc = (efx->flow_control & EFX_FC_RX) ? 1 : 0;
|
||||
|
||||
/* Configure MAC - cut-thru mode is hard wired on */
|
||||
EFX_POPULATE_DWORD_3(reg,
|
||||
XM_RX_JUMBO_MODE, 1,
|
||||
XM_TX_STAT_EN, 1,
|
||||
XM_RX_STAT_EN, 1);
|
||||
falcon_xmac_writel(efx, ®, XM_GLB_CFG_REG_MAC);
|
||||
|
||||
/* Configure TX */
|
||||
EFX_POPULATE_DWORD_6(reg,
|
||||
XM_TXEN, 1,
|
||||
XM_TX_PRMBL, 1,
|
||||
XM_AUTO_PAD, 1,
|
||||
XM_TXCRC, 1,
|
||||
XM_FCNTL, 1,
|
||||
XM_IPG, 0x3);
|
||||
falcon_xmac_writel(efx, ®, XM_TX_CFG_REG_MAC);
|
||||
|
||||
/* Configure RX */
|
||||
EFX_POPULATE_DWORD_5(reg,
|
||||
XM_RXEN, 1,
|
||||
XM_AUTO_DEPAD, 0,
|
||||
XM_ACPT_ALL_MCAST, 1,
|
||||
XM_ACPT_ALL_UCAST, efx->promiscuous,
|
||||
XM_PASS_CRC_ERR, 1);
|
||||
falcon_xmac_writel(efx, ®, XM_RX_CFG_REG_MAC);
|
||||
|
||||
/* Set frame length */
|
||||
max_frame_len = EFX_MAX_FRAME_LEN(efx->net_dev->mtu);
|
||||
EFX_POPULATE_DWORD_1(reg, XM_MAX_RX_FRM_SIZE, max_frame_len);
|
||||
falcon_xmac_writel(efx, ®, XM_RX_PARAM_REG_MAC);
|
||||
EFX_POPULATE_DWORD_2(reg,
|
||||
XM_MAX_TX_FRM_SIZE, max_frame_len,
|
||||
XM_TX_JUMBO_MODE, 1);
|
||||
falcon_xmac_writel(efx, ®, XM_TX_PARAM_REG_MAC);
|
||||
|
||||
EFX_POPULATE_DWORD_2(reg,
|
||||
XM_PAUSE_TIME, 0xfffe, /* MAX PAUSE TIME */
|
||||
XM_DIS_FCNTL, rx_fc ? 0 : 1);
|
||||
falcon_xmac_writel(efx, ®, XM_FC_REG_MAC);
|
||||
|
||||
/* Set MAC address */
|
||||
EFX_POPULATE_DWORD_4(reg,
|
||||
XM_ADR_0, efx->net_dev->dev_addr[0],
|
||||
XM_ADR_1, efx->net_dev->dev_addr[1],
|
||||
XM_ADR_2, efx->net_dev->dev_addr[2],
|
||||
XM_ADR_3, efx->net_dev->dev_addr[3]);
|
||||
falcon_xmac_writel(efx, ®, XM_ADR_LO_REG_MAC);
|
||||
EFX_POPULATE_DWORD_2(reg,
|
||||
XM_ADR_4, efx->net_dev->dev_addr[4],
|
||||
XM_ADR_5, efx->net_dev->dev_addr[5]);
|
||||
falcon_xmac_writel(efx, ®, XM_ADR_HI_REG_MAC);
|
||||
}
|
||||
|
||||
/* Try and bring the Falcon side of the Falcon-Phy XAUI link fails
|
||||
* to come back up. Bash it until it comes back up */
|
||||
static int falcon_check_xaui_link_up(struct efx_nic *efx)
|
||||
{
|
||||
int max_tries, tries;
|
||||
tries = EFX_WORKAROUND_5147(efx) ? 5 : 1;
|
||||
max_tries = tries;
|
||||
|
||||
if (efx->phy_type == PHY_TYPE_NONE)
|
||||
return 0;
|
||||
|
||||
while (tries) {
|
||||
if (falcon_xaui_link_ok(efx))
|
||||
return 1;
|
||||
|
||||
EFX_LOG(efx, "%s Clobbering XAUI (%d tries left).\n",
|
||||
__func__, tries);
|
||||
(void) falcon_reset_xaui(efx);
|
||||
udelay(200);
|
||||
tries--;
|
||||
}
|
||||
|
||||
EFX_ERR(efx, "Failed to bring XAUI link back up in %d tries!\n",
|
||||
max_tries);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void falcon_reconfigure_xmac(struct efx_nic *efx)
|
||||
{
|
||||
int xaui_link_ok;
|
||||
|
||||
falcon_mask_status_intr(efx, 0);
|
||||
|
||||
falcon_deconfigure_mac_wrapper(efx);
|
||||
efx->phy_op->reconfigure(efx);
|
||||
falcon_reconfigure_xmac_core(efx);
|
||||
falcon_reconfigure_mac_wrapper(efx);
|
||||
|
||||
/* Ensure XAUI link is up */
|
||||
xaui_link_ok = falcon_check_xaui_link_up(efx);
|
||||
|
||||
if (xaui_link_ok && efx->link_up)
|
||||
falcon_mask_status_intr(efx, 1);
|
||||
}
|
||||
|
||||
void falcon_fini_xmac(struct efx_nic *efx)
|
||||
{
|
||||
/* Isolate the MAC - PHY */
|
||||
falcon_deconfigure_mac_wrapper(efx);
|
||||
|
||||
/* Potentially power down the PHY */
|
||||
efx->phy_op->fini(efx);
|
||||
}
|
||||
|
||||
void falcon_update_stats_xmac(struct efx_nic *efx)
|
||||
{
|
||||
struct efx_mac_stats *mac_stats = &efx->mac_stats;
|
||||
int rc;
|
||||
|
||||
rc = falcon_dma_stats(efx, XgDmaDone_offset);
|
||||
if (rc)
|
||||
return;
|
||||
|
||||
/* Update MAC stats from DMAed values */
|
||||
FALCON_STAT(efx, XgRxOctets, rx_bytes);
|
||||
FALCON_STAT(efx, XgRxOctetsOK, rx_good_bytes);
|
||||
FALCON_STAT(efx, XgRxPkts, rx_packets);
|
||||
FALCON_STAT(efx, XgRxPktsOK, rx_good);
|
||||
FALCON_STAT(efx, XgRxBroadcastPkts, rx_broadcast);
|
||||
FALCON_STAT(efx, XgRxMulticastPkts, rx_multicast);
|
||||
FALCON_STAT(efx, XgRxUnicastPkts, rx_unicast);
|
||||
FALCON_STAT(efx, XgRxUndersizePkts, rx_lt64);
|
||||
FALCON_STAT(efx, XgRxOversizePkts, rx_gtjumbo);
|
||||
FALCON_STAT(efx, XgRxJabberPkts, rx_bad_gtjumbo);
|
||||
FALCON_STAT(efx, XgRxUndersizeFCSerrorPkts, rx_bad_lt64);
|
||||
FALCON_STAT(efx, XgRxDropEvents, rx_overflow);
|
||||
FALCON_STAT(efx, XgRxFCSerrorPkts, rx_bad);
|
||||
FALCON_STAT(efx, XgRxAlignError, rx_align_error);
|
||||
FALCON_STAT(efx, XgRxSymbolError, rx_symbol_error);
|
||||
FALCON_STAT(efx, XgRxInternalMACError, rx_internal_error);
|
||||
FALCON_STAT(efx, XgRxControlPkts, rx_control);
|
||||
FALCON_STAT(efx, XgRxPausePkts, rx_pause);
|
||||
FALCON_STAT(efx, XgRxPkts64Octets, rx_64);
|
||||
FALCON_STAT(efx, XgRxPkts65to127Octets, rx_65_to_127);
|
||||
FALCON_STAT(efx, XgRxPkts128to255Octets, rx_128_to_255);
|
||||
FALCON_STAT(efx, XgRxPkts256to511Octets, rx_256_to_511);
|
||||
FALCON_STAT(efx, XgRxPkts512to1023Octets, rx_512_to_1023);
|
||||
FALCON_STAT(efx, XgRxPkts1024to15xxOctets, rx_1024_to_15xx);
|
||||
FALCON_STAT(efx, XgRxPkts15xxtoMaxOctets, rx_15xx_to_jumbo);
|
||||
FALCON_STAT(efx, XgRxLengthError, rx_length_error);
|
||||
FALCON_STAT(efx, XgTxPkts, tx_packets);
|
||||
FALCON_STAT(efx, XgTxOctets, tx_bytes);
|
||||
FALCON_STAT(efx, XgTxMulticastPkts, tx_multicast);
|
||||
FALCON_STAT(efx, XgTxBroadcastPkts, tx_broadcast);
|
||||
FALCON_STAT(efx, XgTxUnicastPkts, tx_unicast);
|
||||
FALCON_STAT(efx, XgTxControlPkts, tx_control);
|
||||
FALCON_STAT(efx, XgTxPausePkts, tx_pause);
|
||||
FALCON_STAT(efx, XgTxPkts64Octets, tx_64);
|
||||
FALCON_STAT(efx, XgTxPkts65to127Octets, tx_65_to_127);
|
||||
FALCON_STAT(efx, XgTxPkts128to255Octets, tx_128_to_255);
|
||||
FALCON_STAT(efx, XgTxPkts256to511Octets, tx_256_to_511);
|
||||
FALCON_STAT(efx, XgTxPkts512to1023Octets, tx_512_to_1023);
|
||||
FALCON_STAT(efx, XgTxPkts1024to15xxOctets, tx_1024_to_15xx);
|
||||
FALCON_STAT(efx, XgTxPkts1519toMaxOctets, tx_15xx_to_jumbo);
|
||||
FALCON_STAT(efx, XgTxUndersizePkts, tx_lt64);
|
||||
FALCON_STAT(efx, XgTxOversizePkts, tx_gtjumbo);
|
||||
FALCON_STAT(efx, XgTxNonTcpUdpPkt, tx_non_tcpudp);
|
||||
FALCON_STAT(efx, XgTxMacSrcErrPkt, tx_mac_src_error);
|
||||
FALCON_STAT(efx, XgTxIpSrcErrPkt, tx_ip_src_error);
|
||||
|
||||
/* Update derived statistics */
|
||||
mac_stats->tx_good_bytes =
|
||||
(mac_stats->tx_bytes - mac_stats->tx_bad_bytes);
|
||||
mac_stats->rx_bad_bytes =
|
||||
(mac_stats->rx_bytes - mac_stats->rx_good_bytes);
|
||||
}
|
||||
|
||||
#define EFX_XAUI_RETRAIN_MAX 8
|
||||
|
||||
int falcon_check_xmac(struct efx_nic *efx)
|
||||
{
|
||||
unsigned xaui_link_ok;
|
||||
int rc;
|
||||
|
||||
falcon_mask_status_intr(efx, 0);
|
||||
xaui_link_ok = falcon_xaui_link_ok(efx);
|
||||
|
||||
if (EFX_WORKAROUND_5147(efx) && !xaui_link_ok)
|
||||
(void) falcon_reset_xaui(efx);
|
||||
|
||||
/* Call the PHY check_hw routine */
|
||||
rc = efx->phy_op->check_hw(efx);
|
||||
|
||||
/* Unmask interrupt if everything was (and still is) ok */
|
||||
if (xaui_link_ok && efx->link_up)
|
||||
falcon_mask_status_intr(efx, 1);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Simulate a PHY event */
|
||||
void falcon_xmac_sim_phy_event(struct efx_nic *efx)
|
||||
{
|
||||
efx_qword_t phy_event;
|
||||
|
||||
EFX_POPULATE_QWORD_2(phy_event,
|
||||
EV_CODE, GLOBAL_EV_DECODE,
|
||||
XG_PHY_INTR, 1);
|
||||
falcon_generate_event(&efx->channel[0], &phy_event);
|
||||
}
|
||||
|
||||
int falcon_xmac_get_settings(struct efx_nic *efx, struct ethtool_cmd *ecmd)
|
||||
{
|
||||
mdio_clause45_get_settings(efx, ecmd);
|
||||
ecmd->transceiver = XCVR_INTERNAL;
|
||||
ecmd->phy_address = efx->mii.phy_id;
|
||||
ecmd->autoneg = AUTONEG_DISABLE;
|
||||
ecmd->duplex = DUPLEX_FULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int falcon_xmac_set_settings(struct efx_nic *efx, struct ethtool_cmd *ecmd)
|
||||
{
|
||||
if (ecmd->transceiver != XCVR_INTERNAL)
|
||||
return -EINVAL;
|
||||
if (ecmd->autoneg != AUTONEG_DISABLE)
|
||||
return -EINVAL;
|
||||
if (ecmd->duplex != DUPLEX_FULL)
|
||||
return -EINVAL;
|
||||
|
||||
return mdio_clause45_set_settings(efx, ecmd);
|
||||
}
|
||||
|
||||
|
||||
int falcon_xmac_set_pause(struct efx_nic *efx, enum efx_fc_type flow_control)
|
||||
{
|
||||
int reset;
|
||||
|
||||
if (flow_control & EFX_FC_AUTO) {
|
||||
EFX_LOG(efx, "10G does not support flow control "
|
||||
"autonegotiation\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if ((flow_control & EFX_FC_TX) && !(flow_control & EFX_FC_RX))
|
||||
return -EINVAL;
|
||||
|
||||
/* TX flow control may automatically turn itself off if the
|
||||
* link partner (intermittently) stops responding to pause
|
||||
* frames. There isn't any indication that this has happened,
|
||||
* so the best we do is leave it up to the user to spot this
|
||||
* and fix it be cycling transmit flow control on this end. */
|
||||
reset = ((flow_control & EFX_FC_TX) &&
|
||||
!(efx->flow_control & EFX_FC_TX));
|
||||
if (EFX_WORKAROUND_11482(efx) && reset) {
|
||||
if (FALCON_REV(efx) >= FALCON_REV_B0) {
|
||||
/* Recover by resetting the EM block */
|
||||
if (efx->link_up)
|
||||
falcon_drain_tx_fifo(efx);
|
||||
} else {
|
||||
/* Schedule a reset to recover */
|
||||
efx_schedule_reset(efx, RESET_TYPE_INVISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
efx->flow_control = flow_control;
|
||||
|
||||
return 0;
|
||||
}
|
195
drivers/net/sfc/gmii.h
Normal file
195
drivers/net/sfc/gmii.h
Normal file
@ -0,0 +1,195 @@
|
||||
/****************************************************************************
|
||||
* Driver for Solarflare Solarstorm network controllers and boards
|
||||
* Copyright 2005-2006 Fen Systems Ltd.
|
||||
* Copyright 2006 Solarflare Communications Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published
|
||||
* by the Free Software Foundation, incorporated herein by reference.
|
||||
*/
|
||||
|
||||
#ifndef EFX_GMII_H
|
||||
#define EFX_GMII_H
|
||||
|
||||
/*
|
||||
* GMII interface
|
||||
*/
|
||||
|
||||
#include <linux/mii.h>
|
||||
|
||||
/* GMII registers, excluding registers already defined as MII
|
||||
* registers in mii.h
|
||||
*/
|
||||
#define GMII_IER 0x12 /* Interrupt enable register */
|
||||
#define GMII_ISR 0x13 /* Interrupt status register */
|
||||
|
||||
/* Interrupt enable register */
|
||||
#define IER_ANEG_ERR 0x8000 /* Bit 15 - autonegotiation error */
|
||||
#define IER_SPEED_CHG 0x4000 /* Bit 14 - speed changed */
|
||||
#define IER_DUPLEX_CHG 0x2000 /* Bit 13 - duplex changed */
|
||||
#define IER_PAGE_RCVD 0x1000 /* Bit 12 - page received */
|
||||
#define IER_ANEG_DONE 0x0800 /* Bit 11 - autonegotiation complete */
|
||||
#define IER_LINK_CHG 0x0400 /* Bit 10 - link status changed */
|
||||
#define IER_SYM_ERR 0x0200 /* Bit 9 - symbol error */
|
||||
#define IER_FALSE_CARRIER 0x0100 /* Bit 8 - false carrier */
|
||||
#define IER_FIFO_ERR 0x0080 /* Bit 7 - FIFO over/underflow */
|
||||
#define IER_MDIX_CHG 0x0040 /* Bit 6 - MDI crossover changed */
|
||||
#define IER_DOWNSHIFT 0x0020 /* Bit 5 - downshift */
|
||||
#define IER_ENERGY 0x0010 /* Bit 4 - energy detect */
|
||||
#define IER_DTE_POWER 0x0004 /* Bit 2 - DTE power detect */
|
||||
#define IER_POLARITY_CHG 0x0002 /* Bit 1 - polarity changed */
|
||||
#define IER_JABBER 0x0001 /* Bit 0 - jabber */
|
||||
|
||||
/* Interrupt status register */
|
||||
#define ISR_ANEG_ERR 0x8000 /* Bit 15 - autonegotiation error */
|
||||
#define ISR_SPEED_CHG 0x4000 /* Bit 14 - speed changed */
|
||||
#define ISR_DUPLEX_CHG 0x2000 /* Bit 13 - duplex changed */
|
||||
#define ISR_PAGE_RCVD 0x1000 /* Bit 12 - page received */
|
||||
#define ISR_ANEG_DONE 0x0800 /* Bit 11 - autonegotiation complete */
|
||||
#define ISR_LINK_CHG 0x0400 /* Bit 10 - link status changed */
|
||||
#define ISR_SYM_ERR 0x0200 /* Bit 9 - symbol error */
|
||||
#define ISR_FALSE_CARRIER 0x0100 /* Bit 8 - false carrier */
|
||||
#define ISR_FIFO_ERR 0x0080 /* Bit 7 - FIFO over/underflow */
|
||||
#define ISR_MDIX_CHG 0x0040 /* Bit 6 - MDI crossover changed */
|
||||
#define ISR_DOWNSHIFT 0x0020 /* Bit 5 - downshift */
|
||||
#define ISR_ENERGY 0x0010 /* Bit 4 - energy detect */
|
||||
#define ISR_DTE_POWER 0x0004 /* Bit 2 - DTE power detect */
|
||||
#define ISR_POLARITY_CHG 0x0002 /* Bit 1 - polarity changed */
|
||||
#define ISR_JABBER 0x0001 /* Bit 0 - jabber */
|
||||
|
||||
/* Logically extended advertisement register */
|
||||
#define GM_ADVERTISE_SLCT ADVERTISE_SLCT
|
||||
#define GM_ADVERTISE_CSMA ADVERTISE_CSMA
|
||||
#define GM_ADVERTISE_10HALF ADVERTISE_10HALF
|
||||
#define GM_ADVERTISE_1000XFULL ADVERTISE_1000XFULL
|
||||
#define GM_ADVERTISE_10FULL ADVERTISE_10FULL
|
||||
#define GM_ADVERTISE_1000XHALF ADVERTISE_1000XHALF
|
||||
#define GM_ADVERTISE_100HALF ADVERTISE_100HALF
|
||||
#define GM_ADVERTISE_1000XPAUSE ADVERTISE_1000XPAUSE
|
||||
#define GM_ADVERTISE_100FULL ADVERTISE_100FULL
|
||||
#define GM_ADVERTISE_1000XPSE_ASYM ADVERTISE_1000XPSE_ASYM
|
||||
#define GM_ADVERTISE_100BASE4 ADVERTISE_100BASE4
|
||||
#define GM_ADVERTISE_PAUSE_CAP ADVERTISE_PAUSE_CAP
|
||||
#define GM_ADVERTISE_PAUSE_ASYM ADVERTISE_PAUSE_ASYM
|
||||
#define GM_ADVERTISE_RESV ADVERTISE_RESV
|
||||
#define GM_ADVERTISE_RFAULT ADVERTISE_RFAULT
|
||||
#define GM_ADVERTISE_LPACK ADVERTISE_LPACK
|
||||
#define GM_ADVERTISE_NPAGE ADVERTISE_NPAGE
|
||||
#define GM_ADVERTISE_1000FULL (ADVERTISE_1000FULL << 8)
|
||||
#define GM_ADVERTISE_1000HALF (ADVERTISE_1000HALF << 8)
|
||||
#define GM_ADVERTISE_1000 (GM_ADVERTISE_1000FULL | \
|
||||
GM_ADVERTISE_1000HALF)
|
||||
#define GM_ADVERTISE_FULL (GM_ADVERTISE_1000FULL | \
|
||||
ADVERTISE_FULL)
|
||||
#define GM_ADVERTISE_ALL (GM_ADVERTISE_1000FULL | \
|
||||
GM_ADVERTISE_1000HALF | \
|
||||
ADVERTISE_ALL)
|
||||
|
||||
/* Logically extended link partner ability register */
|
||||
#define GM_LPA_SLCT LPA_SLCT
|
||||
#define GM_LPA_10HALF LPA_10HALF
|
||||
#define GM_LPA_1000XFULL LPA_1000XFULL
|
||||
#define GM_LPA_10FULL LPA_10FULL
|
||||
#define GM_LPA_1000XHALF LPA_1000XHALF
|
||||
#define GM_LPA_100HALF LPA_100HALF
|
||||
#define GM_LPA_1000XPAUSE LPA_1000XPAUSE
|
||||
#define GM_LPA_100FULL LPA_100FULL
|
||||
#define GM_LPA_1000XPAUSE_ASYM LPA_1000XPAUSE_ASYM
|
||||
#define GM_LPA_100BASE4 LPA_100BASE4
|
||||
#define GM_LPA_PAUSE_CAP LPA_PAUSE_CAP
|
||||
#define GM_LPA_PAUSE_ASYM LPA_PAUSE_ASYM
|
||||
#define GM_LPA_RESV LPA_RESV
|
||||
#define GM_LPA_RFAULT LPA_RFAULT
|
||||
#define GM_LPA_LPACK LPA_LPACK
|
||||
#define GM_LPA_NPAGE LPA_NPAGE
|
||||
#define GM_LPA_1000FULL (LPA_1000FULL << 6)
|
||||
#define GM_LPA_1000HALF (LPA_1000HALF << 6)
|
||||
#define GM_LPA_10000FULL 0x00040000
|
||||
#define GM_LPA_10000HALF 0x00080000
|
||||
#define GM_LPA_DUPLEX (GM_LPA_1000FULL | GM_LPA_10000FULL \
|
||||
| LPA_DUPLEX)
|
||||
#define GM_LPA_10 (LPA_10FULL | LPA_10HALF)
|
||||
#define GM_LPA_100 LPA_100
|
||||
#define GM_LPA_1000 (GM_LPA_1000FULL | GM_LPA_1000HALF)
|
||||
#define GM_LPA_10000 (GM_LPA_10000FULL | GM_LPA_10000HALF)
|
||||
|
||||
/* Retrieve GMII autonegotiation advertised abilities
|
||||
*
|
||||
* The MII advertisment register (MII_ADVERTISE) is logically extended
|
||||
* to include advertisement bits ADVERTISE_1000FULL and
|
||||
* ADVERTISE_1000HALF from MII_CTRL1000. The result can be tested
|
||||
* against the GM_ADVERTISE_xxx constants.
|
||||
*/
|
||||
static inline unsigned int gmii_advertised(struct mii_if_info *gmii)
|
||||
{
|
||||
unsigned int advertise;
|
||||
unsigned int ctrl1000;
|
||||
|
||||
advertise = gmii->mdio_read(gmii->dev, gmii->phy_id, MII_ADVERTISE);
|
||||
ctrl1000 = gmii->mdio_read(gmii->dev, gmii->phy_id, MII_CTRL1000);
|
||||
return (((ctrl1000 << 8) & GM_ADVERTISE_1000) | advertise);
|
||||
}
|
||||
|
||||
/* Retrieve GMII autonegotiation link partner abilities
|
||||
*
|
||||
* The MII link partner ability register (MII_LPA) is logically
|
||||
* extended by adding bits LPA_1000HALF and LPA_1000FULL from
|
||||
* MII_STAT1000. The result can be tested against the GM_LPA_xxx
|
||||
* constants.
|
||||
*/
|
||||
static inline unsigned int gmii_lpa(struct mii_if_info *gmii)
|
||||
{
|
||||
unsigned int lpa;
|
||||
unsigned int stat1000;
|
||||
|
||||
lpa = gmii->mdio_read(gmii->dev, gmii->phy_id, MII_LPA);
|
||||
stat1000 = gmii->mdio_read(gmii->dev, gmii->phy_id, MII_STAT1000);
|
||||
return (((stat1000 << 6) & GM_LPA_1000) | lpa);
|
||||
}
|
||||
|
||||
/* Calculate GMII autonegotiated link technology
|
||||
*
|
||||
* "negotiated" should be the result of gmii_advertised() logically
|
||||
* ANDed with the result of gmii_lpa().
|
||||
*
|
||||
* "tech" will be negotiated with the unused bits masked out. For
|
||||
* example, if both ends of the link are capable of both
|
||||
* GM_LPA_1000FULL and GM_LPA_100FULL, GM_LPA_100FULL will be masked
|
||||
* out.
|
||||
*/
|
||||
static inline unsigned int gmii_nway_result(unsigned int negotiated)
|
||||
{
|
||||
unsigned int other_bits;
|
||||
|
||||
/* Mask out the speed and duplexity bits */
|
||||
other_bits = negotiated & ~(GM_LPA_10 | GM_LPA_100 | GM_LPA_1000);
|
||||
|
||||
if (negotiated & GM_LPA_1000FULL)
|
||||
return (other_bits | GM_LPA_1000FULL);
|
||||
else if (negotiated & GM_LPA_1000HALF)
|
||||
return (other_bits | GM_LPA_1000HALF);
|
||||
else
|
||||
return (other_bits | mii_nway_result(negotiated));
|
||||
}
|
||||
|
||||
/* Calculate GMII non-autonegotiated link technology
|
||||
*
|
||||
* This provides an equivalent to gmii_nway_result for the case when
|
||||
* autonegotiation is disabled.
|
||||
*/
|
||||
static inline unsigned int gmii_forced_result(unsigned int bmcr)
|
||||
{
|
||||
unsigned int result;
|
||||
int full_duplex;
|
||||
|
||||
full_duplex = bmcr & BMCR_FULLDPLX;
|
||||
if (bmcr & BMCR_SPEED1000)
|
||||
result = full_duplex ? GM_LPA_1000FULL : GM_LPA_1000HALF;
|
||||
else if (bmcr & BMCR_SPEED100)
|
||||
result = full_duplex ? GM_LPA_100FULL : GM_LPA_100HALF;
|
||||
else
|
||||
result = full_duplex ? GM_LPA_10FULL : GM_LPA_10HALF;
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif /* EFX_GMII_H */
|
381
drivers/net/sfc/i2c-direct.c
Normal file
381
drivers/net/sfc/i2c-direct.c
Normal file
@ -0,0 +1,381 @@
|
||||
/****************************************************************************
|
||||
* Driver for Solarflare Solarstorm network controllers and boards
|
||||
* Copyright 2005 Fen Systems Ltd.
|
||||
* Copyright 2006-2008 Solarflare Communications Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published
|
||||
* by the Free Software Foundation, incorporated herein by reference.
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include "net_driver.h"
|
||||
#include "i2c-direct.h"
|
||||
|
||||
/*
|
||||
* I2C data (SDA) and clock (SCL) line read/writes with appropriate
|
||||
* delays.
|
||||
*/
|
||||
|
||||
static inline void setsda(struct efx_i2c_interface *i2c, int state)
|
||||
{
|
||||
udelay(i2c->op->udelay);
|
||||
i2c->sda = state;
|
||||
i2c->op->setsda(i2c);
|
||||
udelay(i2c->op->udelay);
|
||||
}
|
||||
|
||||
static inline void setscl(struct efx_i2c_interface *i2c, int state)
|
||||
{
|
||||
udelay(i2c->op->udelay);
|
||||
i2c->scl = state;
|
||||
i2c->op->setscl(i2c);
|
||||
udelay(i2c->op->udelay);
|
||||
}
|
||||
|
||||
static inline int getsda(struct efx_i2c_interface *i2c)
|
||||
{
|
||||
int sda;
|
||||
|
||||
udelay(i2c->op->udelay);
|
||||
sda = i2c->op->getsda(i2c);
|
||||
udelay(i2c->op->udelay);
|
||||
return sda;
|
||||
}
|
||||
|
||||
static inline int getscl(struct efx_i2c_interface *i2c)
|
||||
{
|
||||
int scl;
|
||||
|
||||
udelay(i2c->op->udelay);
|
||||
scl = i2c->op->getscl(i2c);
|
||||
udelay(i2c->op->udelay);
|
||||
return scl;
|
||||
}
|
||||
|
||||
/*
|
||||
* I2C low-level protocol operations
|
||||
*
|
||||
*/
|
||||
|
||||
static inline void i2c_release(struct efx_i2c_interface *i2c)
|
||||
{
|
||||
EFX_WARN_ON_PARANOID(!i2c->scl);
|
||||
EFX_WARN_ON_PARANOID(!i2c->sda);
|
||||
/* Devices may time out if operations do not end */
|
||||
setscl(i2c, 1);
|
||||
setsda(i2c, 1);
|
||||
EFX_BUG_ON_PARANOID(getsda(i2c) != 1);
|
||||
EFX_BUG_ON_PARANOID(getscl(i2c) != 1);
|
||||
}
|
||||
|
||||
static inline void i2c_start(struct efx_i2c_interface *i2c)
|
||||
{
|
||||
/* We may be restarting immediately after a {send,recv}_bit,
|
||||
* so SCL will not necessarily already be high.
|
||||
*/
|
||||
EFX_WARN_ON_PARANOID(!i2c->sda);
|
||||
setscl(i2c, 1);
|
||||
setsda(i2c, 0);
|
||||
setscl(i2c, 0);
|
||||
setsda(i2c, 1);
|
||||
}
|
||||
|
||||
static inline void i2c_send_bit(struct efx_i2c_interface *i2c, int bit)
|
||||
{
|
||||
EFX_WARN_ON_PARANOID(i2c->scl != 0);
|
||||
setsda(i2c, bit);
|
||||
setscl(i2c, 1);
|
||||
setscl(i2c, 0);
|
||||
setsda(i2c, 1);
|
||||
}
|
||||
|
||||
static inline int i2c_recv_bit(struct efx_i2c_interface *i2c)
|
||||
{
|
||||
int bit;
|
||||
|
||||
EFX_WARN_ON_PARANOID(i2c->scl != 0);
|
||||
EFX_WARN_ON_PARANOID(!i2c->sda);
|
||||
setscl(i2c, 1);
|
||||
bit = getsda(i2c);
|
||||
setscl(i2c, 0);
|
||||
return bit;
|
||||
}
|
||||
|
||||
static inline void i2c_stop(struct efx_i2c_interface *i2c)
|
||||
{
|
||||
EFX_WARN_ON_PARANOID(i2c->scl != 0);
|
||||
setsda(i2c, 0);
|
||||
setscl(i2c, 1);
|
||||
setsda(i2c, 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* I2C mid-level protocol operations
|
||||
*
|
||||
*/
|
||||
|
||||
/* Sends a byte via the I2C bus and checks for an acknowledgement from
|
||||
* the slave device.
|
||||
*/
|
||||
static int i2c_send_byte(struct efx_i2c_interface *i2c, u8 byte)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* Send byte */
|
||||
for (i = 0; i < 8; i++) {
|
||||
i2c_send_bit(i2c, !!(byte & 0x80));
|
||||
byte <<= 1;
|
||||
}
|
||||
|
||||
/* Check for acknowledgement from slave */
|
||||
return (i2c_recv_bit(i2c) == 0 ? 0 : -EIO);
|
||||
}
|
||||
|
||||
/* Receives a byte via the I2C bus and sends ACK/NACK to the slave device. */
|
||||
static u8 i2c_recv_byte(struct efx_i2c_interface *i2c, int ack)
|
||||
{
|
||||
u8 value = 0;
|
||||
int i;
|
||||
|
||||
/* Receive byte */
|
||||
for (i = 0; i < 8; i++)
|
||||
value = (value << 1) | i2c_recv_bit(i2c);
|
||||
|
||||
/* Send ACK/NACK */
|
||||
i2c_send_bit(i2c, (ack ? 0 : 1));
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/* Calculate command byte for a read operation */
|
||||
static inline u8 i2c_read_cmd(u8 device_id)
|
||||
{
|
||||
return ((device_id << 1) | 1);
|
||||
}
|
||||
|
||||
/* Calculate command byte for a write operation */
|
||||
static inline u8 i2c_write_cmd(u8 device_id)
|
||||
{
|
||||
return ((device_id << 1) | 0);
|
||||
}
|
||||
|
||||
int efx_i2c_check_presence(struct efx_i2c_interface *i2c, u8 device_id)
|
||||
{
|
||||
int rc;
|
||||
|
||||
/* If someone is driving the bus low we just give up. */
|
||||
if (getsda(i2c) == 0 || getscl(i2c) == 0) {
|
||||
EFX_ERR(i2c->efx, "%s someone is holding the I2C bus low."
|
||||
" Giving up.\n", __func__);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
/* Pretend to initiate a device write */
|
||||
i2c_start(i2c);
|
||||
rc = i2c_send_byte(i2c, i2c_write_cmd(device_id));
|
||||
if (rc)
|
||||
goto out;
|
||||
|
||||
out:
|
||||
i2c_stop(i2c);
|
||||
i2c_release(i2c);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* This performs a fast read of one or more consecutive bytes from an
|
||||
* I2C device. Not all devices support consecutive reads of more than
|
||||
* one byte; for these devices use efx_i2c_read() instead.
|
||||
*/
|
||||
int efx_i2c_fast_read(struct efx_i2c_interface *i2c,
|
||||
u8 device_id, u8 offset, u8 *data, unsigned int len)
|
||||
{
|
||||
int i;
|
||||
int rc;
|
||||
|
||||
EFX_WARN_ON_PARANOID(getsda(i2c) != 1);
|
||||
EFX_WARN_ON_PARANOID(getscl(i2c) != 1);
|
||||
EFX_WARN_ON_PARANOID(data == NULL);
|
||||
EFX_WARN_ON_PARANOID(len < 1);
|
||||
|
||||
/* Select device and starting offset */
|
||||
i2c_start(i2c);
|
||||
rc = i2c_send_byte(i2c, i2c_write_cmd(device_id));
|
||||
if (rc)
|
||||
goto out;
|
||||
rc = i2c_send_byte(i2c, offset);
|
||||
if (rc)
|
||||
goto out;
|
||||
|
||||
/* Read data from device */
|
||||
i2c_start(i2c);
|
||||
rc = i2c_send_byte(i2c, i2c_read_cmd(device_id));
|
||||
if (rc)
|
||||
goto out;
|
||||
for (i = 0; i < (len - 1); i++)
|
||||
/* Read and acknowledge all but the last byte */
|
||||
data[i] = i2c_recv_byte(i2c, 1);
|
||||
/* Read last byte with no acknowledgement */
|
||||
data[i] = i2c_recv_byte(i2c, 0);
|
||||
|
||||
out:
|
||||
i2c_stop(i2c);
|
||||
i2c_release(i2c);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* This performs a fast write of one or more consecutive bytes to an
|
||||
* I2C device. Not all devices support consecutive writes of more
|
||||
* than one byte; for these devices use efx_i2c_write() instead.
|
||||
*/
|
||||
int efx_i2c_fast_write(struct efx_i2c_interface *i2c,
|
||||
u8 device_id, u8 offset,
|
||||
const u8 *data, unsigned int len)
|
||||
{
|
||||
int i;
|
||||
int rc;
|
||||
|
||||
EFX_WARN_ON_PARANOID(getsda(i2c) != 1);
|
||||
EFX_WARN_ON_PARANOID(getscl(i2c) != 1);
|
||||
EFX_WARN_ON_PARANOID(len < 1);
|
||||
|
||||
/* Select device and starting offset */
|
||||
i2c_start(i2c);
|
||||
rc = i2c_send_byte(i2c, i2c_write_cmd(device_id));
|
||||
if (rc)
|
||||
goto out;
|
||||
rc = i2c_send_byte(i2c, offset);
|
||||
if (rc)
|
||||
goto out;
|
||||
|
||||
/* Write data to device */
|
||||
for (i = 0; i < len; i++) {
|
||||
rc = i2c_send_byte(i2c, data[i]);
|
||||
if (rc)
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
i2c_stop(i2c);
|
||||
i2c_release(i2c);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* I2C byte-by-byte read */
|
||||
int efx_i2c_read(struct efx_i2c_interface *i2c,
|
||||
u8 device_id, u8 offset, u8 *data, unsigned int len)
|
||||
{
|
||||
int rc;
|
||||
|
||||
/* i2c_fast_read with length 1 is a single byte read */
|
||||
for (; len > 0; offset++, data++, len--) {
|
||||
rc = efx_i2c_fast_read(i2c, device_id, offset, data, 1);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* I2C byte-by-byte write */
|
||||
int efx_i2c_write(struct efx_i2c_interface *i2c,
|
||||
u8 device_id, u8 offset, const u8 *data, unsigned int len)
|
||||
{
|
||||
int rc;
|
||||
|
||||
/* i2c_fast_write with length 1 is a single byte write */
|
||||
for (; len > 0; offset++, data++, len--) {
|
||||
rc = efx_i2c_fast_write(i2c, device_id, offset, data, 1);
|
||||
if (rc)
|
||||
return rc;
|
||||
mdelay(i2c->op->mdelay);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* This is just a slightly neater wrapper round efx_i2c_fast_write
|
||||
* in the case where the target doesn't take an offset
|
||||
*/
|
||||
int efx_i2c_send_bytes(struct efx_i2c_interface *i2c,
|
||||
u8 device_id, const u8 *data, unsigned int len)
|
||||
{
|
||||
return efx_i2c_fast_write(i2c, device_id, data[0], data + 1, len - 1);
|
||||
}
|
||||
|
||||
/* I2C receiving of bytes - does not send an offset byte */
|
||||
int efx_i2c_recv_bytes(struct efx_i2c_interface *i2c, u8 device_id,
|
||||
u8 *bytes, unsigned int len)
|
||||
{
|
||||
int i;
|
||||
int rc;
|
||||
|
||||
EFX_WARN_ON_PARANOID(getsda(i2c) != 1);
|
||||
EFX_WARN_ON_PARANOID(getscl(i2c) != 1);
|
||||
EFX_WARN_ON_PARANOID(len < 1);
|
||||
|
||||
/* Select device */
|
||||
i2c_start(i2c);
|
||||
|
||||
/* Read data from device */
|
||||
rc = i2c_send_byte(i2c, i2c_read_cmd(device_id));
|
||||
if (rc)
|
||||
goto out;
|
||||
|
||||
for (i = 0; i < (len - 1); i++)
|
||||
/* Read and acknowledge all but the last byte */
|
||||
bytes[i] = i2c_recv_byte(i2c, 1);
|
||||
/* Read last byte with no acknowledgement */
|
||||
bytes[i] = i2c_recv_byte(i2c, 0);
|
||||
|
||||
out:
|
||||
i2c_stop(i2c);
|
||||
i2c_release(i2c);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* SMBus and some I2C devices will time out if the I2C clock is
|
||||
* held low for too long. This is most likely to happen in virtualised
|
||||
* systems (when the entire domain is descheduled) but could in
|
||||
* principle happen due to preemption on any busy system (and given the
|
||||
* potential length of an I2C operation turning preemption off is not
|
||||
* a sensible option). The following functions deal with the failure by
|
||||
* retrying up to a fixed number of times.
|
||||
*/
|
||||
|
||||
#define I2C_MAX_RETRIES (10)
|
||||
|
||||
/* The timeout problem will result in -EIO. If the wrapped function
|
||||
* returns any other error, pass this up and do not retry. */
|
||||
#define RETRY_WRAPPER(_f) \
|
||||
int retries = I2C_MAX_RETRIES; \
|
||||
int rc; \
|
||||
while (retries) { \
|
||||
rc = _f; \
|
||||
if (rc != -EIO) \
|
||||
return rc; \
|
||||
retries--; \
|
||||
} \
|
||||
return rc; \
|
||||
|
||||
int efx_i2c_check_presence_retry(struct efx_i2c_interface *i2c, u8 device_id)
|
||||
{
|
||||
RETRY_WRAPPER(efx_i2c_check_presence(i2c, device_id))
|
||||
}
|
||||
|
||||
int efx_i2c_read_retry(struct efx_i2c_interface *i2c,
|
||||
u8 device_id, u8 offset, u8 *data, unsigned int len)
|
||||
{
|
||||
RETRY_WRAPPER(efx_i2c_read(i2c, device_id, offset, data, len))
|
||||
}
|
||||
|
||||
int efx_i2c_write_retry(struct efx_i2c_interface *i2c,
|
||||
u8 device_id, u8 offset, const u8 *data, unsigned int len)
|
||||
{
|
||||
RETRY_WRAPPER(efx_i2c_write(i2c, device_id, offset, data, len))
|
||||
}
|
91
drivers/net/sfc/i2c-direct.h
Normal file
91
drivers/net/sfc/i2c-direct.h
Normal file
@ -0,0 +1,91 @@
|
||||
/****************************************************************************
|
||||
* Driver for Solarflare Solarstorm network controllers and boards
|
||||
* Copyright 2005 Fen Systems Ltd.
|
||||
* Copyright 2006 Solarflare Communications Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published
|
||||
* by the Free Software Foundation, incorporated herein by reference.
|
||||
*/
|
||||
|
||||
#ifndef EFX_I2C_DIRECT_H
|
||||
#define EFX_I2C_DIRECT_H
|
||||
|
||||
#include "net_driver.h"
|
||||
|
||||
/*
|
||||
* Direct control of an I2C bus
|
||||
*/
|
||||
|
||||
struct efx_i2c_interface;
|
||||
|
||||
/**
|
||||
* struct efx_i2c_bit_operations - I2C bus direct control methods
|
||||
*
|
||||
* I2C bus direct control methods.
|
||||
*
|
||||
* @setsda: Set state of SDA line
|
||||
* @setscl: Set state of SCL line
|
||||
* @getsda: Get state of SDA line
|
||||
* @getscl: Get state of SCL line
|
||||
* @udelay: Delay between each bit operation
|
||||
* @mdelay: Delay between each byte write
|
||||
*/
|
||||
struct efx_i2c_bit_operations {
|
||||
void (*setsda) (struct efx_i2c_interface *i2c);
|
||||
void (*setscl) (struct efx_i2c_interface *i2c);
|
||||
int (*getsda) (struct efx_i2c_interface *i2c);
|
||||
int (*getscl) (struct efx_i2c_interface *i2c);
|
||||
unsigned int udelay;
|
||||
unsigned int mdelay;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct efx_i2c_interface - an I2C interface
|
||||
*
|
||||
* An I2C interface.
|
||||
*
|
||||
* @efx: Attached Efx NIC
|
||||
* @op: I2C bus control methods
|
||||
* @sda: Current output state of SDA line
|
||||
* @scl: Current output state of SCL line
|
||||
*/
|
||||
struct efx_i2c_interface {
|
||||
struct efx_nic *efx;
|
||||
struct efx_i2c_bit_operations *op;
|
||||
unsigned int sda:1;
|
||||
unsigned int scl:1;
|
||||
};
|
||||
|
||||
extern int efx_i2c_check_presence(struct efx_i2c_interface *i2c, u8 device_id);
|
||||
extern int efx_i2c_fast_read(struct efx_i2c_interface *i2c,
|
||||
u8 device_id, u8 offset,
|
||||
u8 *data, unsigned int len);
|
||||
extern int efx_i2c_fast_write(struct efx_i2c_interface *i2c,
|
||||
u8 device_id, u8 offset,
|
||||
const u8 *data, unsigned int len);
|
||||
extern int efx_i2c_read(struct efx_i2c_interface *i2c,
|
||||
u8 device_id, u8 offset, u8 *data, unsigned int len);
|
||||
extern int efx_i2c_write(struct efx_i2c_interface *i2c,
|
||||
u8 device_id, u8 offset,
|
||||
const u8 *data, unsigned int len);
|
||||
|
||||
extern int efx_i2c_send_bytes(struct efx_i2c_interface *i2c, u8 device_id,
|
||||
const u8 *bytes, unsigned int len);
|
||||
|
||||
extern int efx_i2c_recv_bytes(struct efx_i2c_interface *i2c, u8 device_id,
|
||||
u8 *bytes, unsigned int len);
|
||||
|
||||
|
||||
/* Versions of the API that retry on failure. */
|
||||
extern int efx_i2c_check_presence_retry(struct efx_i2c_interface *i2c,
|
||||
u8 device_id);
|
||||
|
||||
extern int efx_i2c_read_retry(struct efx_i2c_interface *i2c,
|
||||
u8 device_id, u8 offset, u8 *data, unsigned int len);
|
||||
|
||||
extern int efx_i2c_write_retry(struct efx_i2c_interface *i2c,
|
||||
u8 device_id, u8 offset,
|
||||
const u8 *data, unsigned int len);
|
||||
|
||||
#endif /* EFX_I2C_DIRECT_H */
|
33
drivers/net/sfc/mac.h
Normal file
33
drivers/net/sfc/mac.h
Normal file
@ -0,0 +1,33 @@
|
||||
/****************************************************************************
|
||||
* Driver for Solarflare Solarstorm network controllers and boards
|
||||
* Copyright 2005-2006 Fen Systems Ltd.
|
||||
* Copyright 2006-2007 Solarflare Communications Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published
|
||||
* by the Free Software Foundation, incorporated herein by reference.
|
||||
*/
|
||||
|
||||
#ifndef EFX_MAC_H
|
||||
#define EFX_MAC_H
|
||||
|
||||
#include "net_driver.h"
|
||||
|
||||
extern void falcon_xmac_writel(struct efx_nic *efx,
|
||||
efx_dword_t *value, unsigned int mac_reg);
|
||||
extern void falcon_xmac_readl(struct efx_nic *efx,
|
||||
efx_dword_t *value, unsigned int mac_reg);
|
||||
extern int falcon_init_xmac(struct efx_nic *efx);
|
||||
extern void falcon_reconfigure_xmac(struct efx_nic *efx);
|
||||
extern void falcon_update_stats_xmac(struct efx_nic *efx);
|
||||
extern void falcon_fini_xmac(struct efx_nic *efx);
|
||||
extern int falcon_check_xmac(struct efx_nic *efx);
|
||||
extern void falcon_xmac_sim_phy_event(struct efx_nic *efx);
|
||||
extern int falcon_xmac_get_settings(struct efx_nic *efx,
|
||||
struct ethtool_cmd *ecmd);
|
||||
extern int falcon_xmac_set_settings(struct efx_nic *efx,
|
||||
struct ethtool_cmd *ecmd);
|
||||
extern int falcon_xmac_set_pause(struct efx_nic *efx,
|
||||
enum efx_fc_type pause_params);
|
||||
|
||||
#endif
|
282
drivers/net/sfc/mdio_10g.c
Normal file
282
drivers/net/sfc/mdio_10g.c
Normal file
@ -0,0 +1,282 @@
|
||||
/****************************************************************************
|
||||
* Driver for Solarflare Solarstorm network controllers and boards
|
||||
* Copyright 2006-2008 Solarflare Communications Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published
|
||||
* by the Free Software Foundation, incorporated herein by reference.
|
||||
*/
|
||||
/*
|
||||
* Useful functions for working with MDIO clause 45 PHYs
|
||||
*/
|
||||
#include <linux/types.h>
|
||||
#include <linux/ethtool.h>
|
||||
#include <linux/delay.h>
|
||||
#include "net_driver.h"
|
||||
#include "mdio_10g.h"
|
||||
#include "boards.h"
|
||||
|
||||
int mdio_clause45_reset_mmd(struct efx_nic *port, int mmd,
|
||||
int spins, int spintime)
|
||||
{
|
||||
u32 ctrl;
|
||||
int phy_id = port->mii.phy_id;
|
||||
|
||||
/* Catch callers passing values in the wrong units (or just silly) */
|
||||
EFX_BUG_ON_PARANOID(spins * spintime >= 5000);
|
||||
|
||||
mdio_clause45_write(port, phy_id, mmd, MDIO_MMDREG_CTRL1,
|
||||
(1 << MDIO_MMDREG_CTRL1_RESET_LBN));
|
||||
/* Wait for the reset bit to clear. */
|
||||
do {
|
||||
msleep(spintime);
|
||||
ctrl = mdio_clause45_read(port, phy_id, mmd, MDIO_MMDREG_CTRL1);
|
||||
spins--;
|
||||
|
||||
} while (spins && (ctrl & (1 << MDIO_MMDREG_CTRL1_RESET_LBN)));
|
||||
|
||||
return spins ? spins : -ETIMEDOUT;
|
||||
}
|
||||
|
||||
static int mdio_clause45_check_mmd(struct efx_nic *efx, int mmd,
|
||||
int fault_fatal)
|
||||
{
|
||||
int status;
|
||||
int phy_id = efx->mii.phy_id;
|
||||
|
||||
/* Read MMD STATUS2 to check it is responding. */
|
||||
status = mdio_clause45_read(efx, phy_id, mmd, MDIO_MMDREG_STAT2);
|
||||
if (((status >> MDIO_MMDREG_STAT2_PRESENT_LBN) &
|
||||
((1 << MDIO_MMDREG_STAT2_PRESENT_WIDTH) - 1)) !=
|
||||
MDIO_MMDREG_STAT2_PRESENT_VAL) {
|
||||
EFX_ERR(efx, "PHY MMD %d not responding.\n", mmd);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* Read MMD STATUS 1 to check for fault. */
|
||||
status = mdio_clause45_read(efx, phy_id, mmd, MDIO_MMDREG_STAT1);
|
||||
if ((status & (1 << MDIO_MMDREG_STAT1_FAULT_LBN)) != 0) {
|
||||
if (fault_fatal) {
|
||||
EFX_ERR(efx, "PHY MMD %d reporting fatal"
|
||||
" fault: status %x\n", mmd, status);
|
||||
return -EIO;
|
||||
} else {
|
||||
EFX_LOG(efx, "PHY MMD %d reporting status"
|
||||
" %x (expected)\n", mmd, status);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* This ought to be ridiculous overkill. We expect it to fail rarely */
|
||||
#define MDIO45_RESET_TIME 1000 /* ms */
|
||||
#define MDIO45_RESET_ITERS 100
|
||||
|
||||
int mdio_clause45_wait_reset_mmds(struct efx_nic *efx,
|
||||
unsigned int mmd_mask)
|
||||
{
|
||||
const int spintime = MDIO45_RESET_TIME / MDIO45_RESET_ITERS;
|
||||
int tries = MDIO45_RESET_ITERS;
|
||||
int rc = 0;
|
||||
int in_reset;
|
||||
|
||||
while (tries) {
|
||||
int mask = mmd_mask;
|
||||
int mmd = 0;
|
||||
int stat;
|
||||
in_reset = 0;
|
||||
while (mask) {
|
||||
if (mask & 1) {
|
||||
stat = mdio_clause45_read(efx,
|
||||
efx->mii.phy_id,
|
||||
mmd,
|
||||
MDIO_MMDREG_CTRL1);
|
||||
if (stat < 0) {
|
||||
EFX_ERR(efx, "failed to read status of"
|
||||
" MMD %d\n", mmd);
|
||||
return -EIO;
|
||||
}
|
||||
if (stat & (1 << MDIO_MMDREG_CTRL1_RESET_LBN))
|
||||
in_reset |= (1 << mmd);
|
||||
}
|
||||
mask = mask >> 1;
|
||||
mmd++;
|
||||
}
|
||||
if (!in_reset)
|
||||
break;
|
||||
tries--;
|
||||
msleep(spintime);
|
||||
}
|
||||
if (in_reset != 0) {
|
||||
EFX_ERR(efx, "not all MMDs came out of reset in time."
|
||||
" MMDs still in reset: %x\n", in_reset);
|
||||
rc = -ETIMEDOUT;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
int mdio_clause45_check_mmds(struct efx_nic *efx,
|
||||
unsigned int mmd_mask, unsigned int fatal_mask)
|
||||
{
|
||||
int devices, mmd = 0;
|
||||
int probe_mmd;
|
||||
|
||||
/* Historically we have probed the PHYXS to find out what devices are
|
||||
* present,but that doesn't work so well if the PHYXS isn't expected
|
||||
* to exist, if so just find the first item in the list supplied. */
|
||||
probe_mmd = (mmd_mask & MDIO_MMDREG_DEVS0_PHYXS) ? MDIO_MMD_PHYXS :
|
||||
__ffs(mmd_mask);
|
||||
devices = mdio_clause45_read(efx, efx->mii.phy_id,
|
||||
probe_mmd, MDIO_MMDREG_DEVS0);
|
||||
|
||||
/* Check all the expected MMDs are present */
|
||||
if (devices < 0) {
|
||||
EFX_ERR(efx, "failed to read devices present\n");
|
||||
return -EIO;
|
||||
}
|
||||
if ((devices & mmd_mask) != mmd_mask) {
|
||||
EFX_ERR(efx, "required MMDs not present: got %x, "
|
||||
"wanted %x\n", devices, mmd_mask);
|
||||
return -ENODEV;
|
||||
}
|
||||
EFX_TRACE(efx, "Devices present: %x\n", devices);
|
||||
|
||||
/* Check all required MMDs are responding and happy. */
|
||||
while (mmd_mask) {
|
||||
if (mmd_mask & 1) {
|
||||
int fault_fatal = fatal_mask & 1;
|
||||
if (mdio_clause45_check_mmd(efx, mmd, fault_fatal))
|
||||
return -EIO;
|
||||
}
|
||||
mmd_mask = mmd_mask >> 1;
|
||||
fatal_mask = fatal_mask >> 1;
|
||||
mmd++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mdio_clause45_links_ok(struct efx_nic *efx, unsigned int mmd_mask)
|
||||
{
|
||||
int phy_id = efx->mii.phy_id;
|
||||
int status;
|
||||
int ok = 1;
|
||||
int mmd = 0;
|
||||
int good;
|
||||
|
||||
while (mmd_mask) {
|
||||
if (mmd_mask & 1) {
|
||||
/* Double reads because link state is latched, and a
|
||||
* read moves the current state into the register */
|
||||
status = mdio_clause45_read(efx, phy_id,
|
||||
mmd, MDIO_MMDREG_STAT1);
|
||||
status = mdio_clause45_read(efx, phy_id,
|
||||
mmd, MDIO_MMDREG_STAT1);
|
||||
|
||||
good = status & (1 << MDIO_MMDREG_STAT1_LINK_LBN);
|
||||
ok = ok && good;
|
||||
}
|
||||
mmd_mask = (mmd_mask >> 1);
|
||||
mmd++;
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
/**
|
||||
* mdio_clause45_get_settings - Read (some of) the PHY settings over MDIO.
|
||||
* @efx: Efx NIC
|
||||
* @ecmd: Buffer for settings
|
||||
*
|
||||
* On return the 'port', 'speed', 'supported' and 'advertising' fields of
|
||||
* ecmd have been filled out based on the PMA type.
|
||||
*/
|
||||
void mdio_clause45_get_settings(struct efx_nic *efx,
|
||||
struct ethtool_cmd *ecmd)
|
||||
{
|
||||
int pma_type;
|
||||
|
||||
/* If no PMA is present we are presumably talking something XAUI-ish
|
||||
* like CX4. Which we report as FIBRE (see below) */
|
||||
if ((efx->phy_op->mmds & DEV_PRESENT_BIT(MDIO_MMD_PMAPMD)) == 0) {
|
||||
ecmd->speed = SPEED_10000;
|
||||
ecmd->port = PORT_FIBRE;
|
||||
ecmd->supported = SUPPORTED_FIBRE;
|
||||
ecmd->advertising = ADVERTISED_FIBRE;
|
||||
return;
|
||||
}
|
||||
|
||||
pma_type = mdio_clause45_read(efx, efx->mii.phy_id,
|
||||
MDIO_MMD_PMAPMD, MDIO_MMDREG_CTRL2);
|
||||
pma_type &= MDIO_PMAPMD_CTRL2_TYPE_MASK;
|
||||
|
||||
switch (pma_type) {
|
||||
/* We represent CX4 as fibre in the absence of anything
|
||||
better. */
|
||||
case MDIO_PMAPMD_CTRL2_10G_CX4:
|
||||
ecmd->speed = SPEED_10000;
|
||||
ecmd->port = PORT_FIBRE;
|
||||
ecmd->supported = SUPPORTED_FIBRE;
|
||||
ecmd->advertising = ADVERTISED_FIBRE;
|
||||
break;
|
||||
/* 10G Base-T */
|
||||
case MDIO_PMAPMD_CTRL2_10G_BT:
|
||||
ecmd->speed = SPEED_10000;
|
||||
ecmd->port = PORT_TP;
|
||||
ecmd->supported = SUPPORTED_TP | SUPPORTED_10000baseT_Full;
|
||||
ecmd->advertising = (ADVERTISED_FIBRE
|
||||
| ADVERTISED_10000baseT_Full);
|
||||
break;
|
||||
case MDIO_PMAPMD_CTRL2_1G_BT:
|
||||
ecmd->speed = SPEED_1000;
|
||||
ecmd->port = PORT_TP;
|
||||
ecmd->supported = SUPPORTED_TP | SUPPORTED_1000baseT_Full;
|
||||
ecmd->advertising = (ADVERTISED_FIBRE
|
||||
| ADVERTISED_1000baseT_Full);
|
||||
break;
|
||||
case MDIO_PMAPMD_CTRL2_100_BT:
|
||||
ecmd->speed = SPEED_100;
|
||||
ecmd->port = PORT_TP;
|
||||
ecmd->supported = SUPPORTED_TP | SUPPORTED_100baseT_Full;
|
||||
ecmd->advertising = (ADVERTISED_FIBRE
|
||||
| ADVERTISED_100baseT_Full);
|
||||
break;
|
||||
case MDIO_PMAPMD_CTRL2_10_BT:
|
||||
ecmd->speed = SPEED_10;
|
||||
ecmd->port = PORT_TP;
|
||||
ecmd->supported = SUPPORTED_TP | SUPPORTED_10baseT_Full;
|
||||
ecmd->advertising = ADVERTISED_FIBRE | ADVERTISED_10baseT_Full;
|
||||
break;
|
||||
/* All the other defined modes are flavours of
|
||||
* 10G optical */
|
||||
default:
|
||||
ecmd->speed = SPEED_10000;
|
||||
ecmd->port = PORT_FIBRE;
|
||||
ecmd->supported = SUPPORTED_FIBRE;
|
||||
ecmd->advertising = ADVERTISED_FIBRE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* mdio_clause45_set_settings - Set (some of) the PHY settings over MDIO.
|
||||
* @efx: Efx NIC
|
||||
* @ecmd: New settings
|
||||
*
|
||||
* Currently this just enforces that we are _not_ changing the
|
||||
* 'port', 'speed', 'supported' or 'advertising' settings as these
|
||||
* cannot be changed on any currently supported PHY.
|
||||
*/
|
||||
int mdio_clause45_set_settings(struct efx_nic *efx,
|
||||
struct ethtool_cmd *ecmd)
|
||||
{
|
||||
struct ethtool_cmd tmpcmd;
|
||||
mdio_clause45_get_settings(efx, &tmpcmd);
|
||||
/* None of the current PHYs support more than one mode
|
||||
* of operation (and only 10GBT ever will), so keep things
|
||||
* simple for now */
|
||||
if ((ecmd->speed == tmpcmd.speed) && (ecmd->port == tmpcmd.port) &&
|
||||
(ecmd->supported == tmpcmd.supported) &&
|
||||
(ecmd->advertising == tmpcmd.advertising))
|
||||
return 0;
|
||||
return -EOPNOTSUPP;
|
||||
}
|
232
drivers/net/sfc/mdio_10g.h
Normal file
232
drivers/net/sfc/mdio_10g.h
Normal file
@ -0,0 +1,232 @@
|
||||
/****************************************************************************
|
||||
* Driver for Solarflare Solarstorm network controllers and boards
|
||||
* Copyright 2006-2008 Solarflare Communications Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published
|
||||
* by the Free Software Foundation, incorporated herein by reference.
|
||||
*/
|
||||
|
||||
#ifndef EFX_MDIO_10G_H
|
||||
#define EFX_MDIO_10G_H
|
||||
|
||||
/*
|
||||
* Definitions needed for doing 10G MDIO as specified in clause 45
|
||||
* MDIO, which do not appear in Linux yet. Also some helper functions.
|
||||
*/
|
||||
|
||||
#include "efx.h"
|
||||
#include "boards.h"
|
||||
|
||||
/* Numbering of the MDIO Manageable Devices (MMDs) */
|
||||
/* Physical Medium Attachment/ Physical Medium Dependent sublayer */
|
||||
#define MDIO_MMD_PMAPMD (1)
|
||||
/* WAN Interface Sublayer */
|
||||
#define MDIO_MMD_WIS (2)
|
||||
/* Physical Coding Sublayer */
|
||||
#define MDIO_MMD_PCS (3)
|
||||
/* PHY Extender Sublayer */
|
||||
#define MDIO_MMD_PHYXS (4)
|
||||
/* Extender Sublayer */
|
||||
#define MDIO_MMD_DTEXS (5)
|
||||
/* Transmission convergence */
|
||||
#define MDIO_MMD_TC (6)
|
||||
/* Auto negotiation */
|
||||
#define MDIO_MMD_AN (7)
|
||||
|
||||
/* Generic register locations */
|
||||
#define MDIO_MMDREG_CTRL1 (0)
|
||||
#define MDIO_MMDREG_STAT1 (1)
|
||||
#define MDIO_MMDREG_IDHI (2)
|
||||
#define MDIO_MMDREG_IDLOW (3)
|
||||
#define MDIO_MMDREG_SPEED (4)
|
||||
#define MDIO_MMDREG_DEVS0 (5)
|
||||
#define MDIO_MMDREG_DEVS1 (6)
|
||||
#define MDIO_MMDREG_CTRL2 (7)
|
||||
#define MDIO_MMDREG_STAT2 (8)
|
||||
|
||||
/* Bits in MMDREG_CTRL1 */
|
||||
/* Reset */
|
||||
#define MDIO_MMDREG_CTRL1_RESET_LBN (15)
|
||||
#define MDIO_MMDREG_CTRL1_RESET_WIDTH (1)
|
||||
|
||||
/* Bits in MMDREG_STAT1 */
|
||||
#define MDIO_MMDREG_STAT1_FAULT_LBN (7)
|
||||
#define MDIO_MMDREG_STAT1_FAULT_WIDTH (1)
|
||||
/* Link state */
|
||||
#define MDIO_MMDREG_STAT1_LINK_LBN (2)
|
||||
#define MDIO_MMDREG_STAT1_LINK_WIDTH (1)
|
||||
|
||||
/* Bits in ID reg */
|
||||
#define MDIO_ID_REV(_id32) (_id32 & 0xf)
|
||||
#define MDIO_ID_MODEL(_id32) ((_id32 >> 4) & 0x3f)
|
||||
#define MDIO_ID_OUI(_id32) (_id32 >> 10)
|
||||
|
||||
/* Bits in MMDREG_DEVS0. Someone thoughtfully layed things out
|
||||
* so the 'bit present' bit number of an MMD is the number of
|
||||
* that MMD */
|
||||
#define DEV_PRESENT_BIT(_b) (1 << _b)
|
||||
|
||||
#define MDIO_MMDREG_DEVS0_PHYXS DEV_PRESENT_BIT(MDIO_MMD_PHYXS)
|
||||
#define MDIO_MMDREG_DEVS0_PCS DEV_PRESENT_BIT(MDIO_MMD_PCS)
|
||||
#define MDIO_MMDREG_DEVS0_PMAPMD DEV_PRESENT_BIT(MDIO_MMD_PMAPMD)
|
||||
|
||||
/* Bits in MMDREG_STAT2 */
|
||||
#define MDIO_MMDREG_STAT2_PRESENT_VAL (2)
|
||||
#define MDIO_MMDREG_STAT2_PRESENT_LBN (14)
|
||||
#define MDIO_MMDREG_STAT2_PRESENT_WIDTH (2)
|
||||
|
||||
/* PMA type (4 bits) */
|
||||
#define MDIO_PMAPMD_CTRL2_10G_CX4 (0x0)
|
||||
#define MDIO_PMAPMD_CTRL2_10G_EW (0x1)
|
||||
#define MDIO_PMAPMD_CTRL2_10G_LW (0x2)
|
||||
#define MDIO_PMAPMD_CTRL2_10G_SW (0x3)
|
||||
#define MDIO_PMAPMD_CTRL2_10G_LX4 (0x4)
|
||||
#define MDIO_PMAPMD_CTRL2_10G_ER (0x5)
|
||||
#define MDIO_PMAPMD_CTRL2_10G_LR (0x6)
|
||||
#define MDIO_PMAPMD_CTRL2_10G_SR (0x7)
|
||||
/* Reserved */
|
||||
#define MDIO_PMAPMD_CTRL2_10G_BT (0x9)
|
||||
/* Reserved */
|
||||
/* Reserved */
|
||||
#define MDIO_PMAPMD_CTRL2_1G_BT (0xc)
|
||||
/* Reserved */
|
||||
#define MDIO_PMAPMD_CTRL2_100_BT (0xe)
|
||||
#define MDIO_PMAPMD_CTRL2_10_BT (0xf)
|
||||
#define MDIO_PMAPMD_CTRL2_TYPE_MASK (0xf)
|
||||
|
||||
/* /\* PHY XGXS lane state *\/ */
|
||||
#define MDIO_PHYXS_LANE_STATE (0x18)
|
||||
#define MDIO_PHYXS_LANE_ALIGNED_LBN (12)
|
||||
|
||||
/* AN registers */
|
||||
#define MDIO_AN_STATUS (1)
|
||||
#define MDIO_AN_STATUS_XNP_LBN (7)
|
||||
#define MDIO_AN_STATUS_PAGE_LBN (6)
|
||||
#define MDIO_AN_STATUS_AN_DONE_LBN (5)
|
||||
#define MDIO_AN_STATUS_LP_AN_CAP_LBN (0)
|
||||
|
||||
#define MDIO_AN_10GBT_STATUS (33)
|
||||
#define MDIO_AN_10GBT_STATUS_MS_FLT_LBN (15) /* MASTER/SLAVE config fault */
|
||||
#define MDIO_AN_10GBT_STATUS_MS_LBN (14) /* MASTER/SLAVE config */
|
||||
#define MDIO_AN_10GBT_STATUS_LOC_OK_LBN (13) /* Local OK */
|
||||
#define MDIO_AN_10GBT_STATUS_REM_OK_LBN (12) /* Remote OK */
|
||||
#define MDIO_AN_10GBT_STATUS_LP_10G_LBN (11) /* Link partner is 10GBT capable */
|
||||
#define MDIO_AN_10GBT_STATUS_LP_LTA_LBN (10) /* LP loop timing ability */
|
||||
#define MDIO_AN_10GBT_STATUS_LP_TRR_LBN (9) /* LP Training Reset Request */
|
||||
|
||||
|
||||
/* Packing of the prt and dev arguments of clause 45 style MDIO into a
|
||||
* single int so they can be passed into the mdio_read/write functions
|
||||
* that currently exist. Note that as Falcon is the only current user,
|
||||
* the packed form is chosen to match what Falcon needs to write into
|
||||
* a register. This is checked at compile-time so do not change it. If
|
||||
* your target chip needs things layed out differently you will need
|
||||
* to unpack the arguments in your chip-specific mdio functions.
|
||||
*/
|
||||
/* These are defined by the standard. */
|
||||
#define MDIO45_PRT_ID_WIDTH (5)
|
||||
#define MDIO45_DEV_ID_WIDTH (5)
|
||||
|
||||
/* The prt ID is just packed in immediately to the left of the dev ID */
|
||||
#define MDIO45_PRT_DEV_WIDTH (MDIO45_PRT_ID_WIDTH + MDIO45_DEV_ID_WIDTH)
|
||||
|
||||
#define MDIO45_PRT_ID_MASK ((1 << MDIO45_PRT_DEV_WIDTH) - 1)
|
||||
/* This is the prt + dev extended by 1 bit to hold the 'is clause 45' flag. */
|
||||
#define MDIO45_XPRT_ID_WIDTH (MDIO45_PRT_DEV_WIDTH + 1)
|
||||
#define MDIO45_XPRT_ID_MASK ((1 << MDIO45_XPRT_ID_WIDTH) - 1)
|
||||
#define MDIO45_XPRT_ID_IS10G (1 << (MDIO45_XPRT_ID_WIDTH - 1))
|
||||
|
||||
|
||||
#define MDIO45_PRT_ID_COMP_LBN MDIO45_DEV_ID_WIDTH
|
||||
#define MDIO45_PRT_ID_COMP_WIDTH MDIO45_PRT_ID_WIDTH
|
||||
#define MDIO45_DEV_ID_COMP_LBN 0
|
||||
#define MDIO45_DEV_ID_COMP_WIDTH MDIO45_DEV_ID_WIDTH
|
||||
|
||||
/* Compose port and device into a phy_id */
|
||||
static inline int mdio_clause45_pack(u8 prt, u8 dev)
|
||||
{
|
||||
efx_dword_t phy_id;
|
||||
EFX_POPULATE_DWORD_2(phy_id, MDIO45_PRT_ID_COMP, prt,
|
||||
MDIO45_DEV_ID_COMP, dev);
|
||||
return MDIO45_XPRT_ID_IS10G | EFX_DWORD_VAL(phy_id);
|
||||
}
|
||||
|
||||
static inline void mdio_clause45_unpack(u32 val, u8 *prt, u8 *dev)
|
||||
{
|
||||
efx_dword_t phy_id;
|
||||
EFX_POPULATE_DWORD_1(phy_id, EFX_DWORD_0, val);
|
||||
*prt = EFX_DWORD_FIELD(phy_id, MDIO45_PRT_ID_COMP);
|
||||
*dev = EFX_DWORD_FIELD(phy_id, MDIO45_DEV_ID_COMP);
|
||||
}
|
||||
|
||||
static inline int mdio_clause45_read(struct efx_nic *efx,
|
||||
u8 prt, u8 dev, u16 addr)
|
||||
{
|
||||
return efx->mii.mdio_read(efx->net_dev,
|
||||
mdio_clause45_pack(prt, dev), addr);
|
||||
}
|
||||
|
||||
static inline void mdio_clause45_write(struct efx_nic *efx,
|
||||
u8 prt, u8 dev, u16 addr, int value)
|
||||
{
|
||||
efx->mii.mdio_write(efx->net_dev,
|
||||
mdio_clause45_pack(prt, dev), addr, value);
|
||||
}
|
||||
|
||||
|
||||
static inline u32 mdio_clause45_read_id(struct efx_nic *efx, int mmd)
|
||||
{
|
||||
int phy_id = efx->mii.phy_id;
|
||||
u16 id_low = mdio_clause45_read(efx, phy_id, mmd, MDIO_MMDREG_IDLOW);
|
||||
u16 id_hi = mdio_clause45_read(efx, phy_id, mmd, MDIO_MMDREG_IDHI);
|
||||
return (id_hi << 16) | (id_low);
|
||||
}
|
||||
|
||||
static inline int mdio_clause45_phyxgxs_lane_sync(struct efx_nic *efx)
|
||||
{
|
||||
int i, sync, lane_status;
|
||||
|
||||
for (i = 0; i < 2; ++i)
|
||||
lane_status = mdio_clause45_read(efx, efx->mii.phy_id,
|
||||
MDIO_MMD_PHYXS,
|
||||
MDIO_PHYXS_LANE_STATE);
|
||||
|
||||
sync = (lane_status & (1 << MDIO_PHYXS_LANE_ALIGNED_LBN)) != 0;
|
||||
if (!sync)
|
||||
EFX_INFO(efx, "XGXS lane status: %x\n", lane_status);
|
||||
return sync;
|
||||
}
|
||||
|
||||
extern const char *mdio_clause45_mmd_name(int mmd);
|
||||
|
||||
/*
|
||||
* Reset a specific MMD and wait for reset to clear.
|
||||
* Return number of spins left (>0) on success, -%ETIMEDOUT on failure.
|
||||
*
|
||||
* This function will sleep
|
||||
*/
|
||||
extern int mdio_clause45_reset_mmd(struct efx_nic *efx, int mmd,
|
||||
int spins, int spintime);
|
||||
|
||||
/* As mdio_clause45_check_mmd but for multiple MMDs */
|
||||
int mdio_clause45_check_mmds(struct efx_nic *efx,
|
||||
unsigned int mmd_mask, unsigned int fatal_mask);
|
||||
|
||||
/* Check the link status of specified mmds in bit mask */
|
||||
extern int mdio_clause45_links_ok(struct efx_nic *efx,
|
||||
unsigned int mmd_mask);
|
||||
|
||||
/* Read (some of) the PHY settings over MDIO */
|
||||
extern void mdio_clause45_get_settings(struct efx_nic *efx,
|
||||
struct ethtool_cmd *ecmd);
|
||||
|
||||
/* Set (some of) the PHY settings over MDIO */
|
||||
extern int mdio_clause45_set_settings(struct efx_nic *efx,
|
||||
struct ethtool_cmd *ecmd);
|
||||
|
||||
/* Wait for specified MMDs to exit reset within a timeout */
|
||||
extern int mdio_clause45_wait_reset_mmds(struct efx_nic *efx,
|
||||
unsigned int mmd_mask);
|
||||
|
||||
#endif /* EFX_MDIO_10G_H */
|
883
drivers/net/sfc/net_driver.h
Normal file
883
drivers/net/sfc/net_driver.h
Normal file
@ -0,0 +1,883 @@
|
||||
/****************************************************************************
|
||||
* Driver for Solarflare Solarstorm network controllers and boards
|
||||
* Copyright 2005-2006 Fen Systems Ltd.
|
||||
* Copyright 2005-2008 Solarflare Communications Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published
|
||||
* by the Free Software Foundation, incorporated herein by reference.
|
||||
*/
|
||||
|
||||
/* Common definitions for all Efx net driver code */
|
||||
|
||||
#ifndef EFX_NET_DRIVER_H
|
||||
#define EFX_NET_DRIVER_H
|
||||
|
||||
#include <linux/version.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/ethtool.h>
|
||||
#include <linux/if_vlan.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/mii.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/highmem.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/inet_lro.h>
|
||||
|
||||
#include "enum.h"
|
||||
#include "bitfield.h"
|
||||
#include "i2c-direct.h"
|
||||
|
||||
#define EFX_MAX_LRO_DESCRIPTORS 8
|
||||
#define EFX_MAX_LRO_AGGR MAX_SKB_FRAGS
|
||||
|
||||
/**************************************************************************
|
||||
*
|
||||
* Build definitions
|
||||
*
|
||||
**************************************************************************/
|
||||
#ifndef EFX_DRIVER_NAME
|
||||
#define EFX_DRIVER_NAME "sfc"
|
||||
#endif
|
||||
#define EFX_DRIVER_VERSION "2.2.0136"
|
||||
|
||||
#ifdef EFX_ENABLE_DEBUG
|
||||
#define EFX_BUG_ON_PARANOID(x) BUG_ON(x)
|
||||
#define EFX_WARN_ON_PARANOID(x) WARN_ON(x)
|
||||
#else
|
||||
#define EFX_BUG_ON_PARANOID(x) do {} while (0)
|
||||
#define EFX_WARN_ON_PARANOID(x) do {} while (0)
|
||||
#endif
|
||||
|
||||
#define NET_DEV_REGISTERED(efx) \
|
||||
((efx)->net_dev->reg_state == NETREG_REGISTERED)
|
||||
|
||||
/* Include net device name in log messages if it has been registered.
|
||||
* Use efx->name not efx->net_dev->name so that races with (un)registration
|
||||
* are harmless.
|
||||
*/
|
||||
#define NET_DEV_NAME(efx) (NET_DEV_REGISTERED(efx) ? (efx)->name : "")
|
||||
|
||||
/* Un-rate-limited logging */
|
||||
#define EFX_ERR(efx, fmt, args...) \
|
||||
dev_err(&((efx)->pci_dev->dev), "ERR: %s " fmt, NET_DEV_NAME(efx), ##args)
|
||||
|
||||
#define EFX_INFO(efx, fmt, args...) \
|
||||
dev_info(&((efx)->pci_dev->dev), "INFO: %s " fmt, NET_DEV_NAME(efx), ##args)
|
||||
|
||||
#ifdef EFX_ENABLE_DEBUG
|
||||
#define EFX_LOG(efx, fmt, args...) \
|
||||
dev_info(&((efx)->pci_dev->dev), "DBG: %s " fmt, NET_DEV_NAME(efx), ##args)
|
||||
#else
|
||||
#define EFX_LOG(efx, fmt, args...) \
|
||||
dev_dbg(&((efx)->pci_dev->dev), "DBG: %s " fmt, NET_DEV_NAME(efx), ##args)
|
||||
#endif
|
||||
|
||||
#define EFX_TRACE(efx, fmt, args...) do {} while (0)
|
||||
|
||||
#define EFX_REGDUMP(efx, fmt, args...) do {} while (0)
|
||||
|
||||
/* Rate-limited logging */
|
||||
#define EFX_ERR_RL(efx, fmt, args...) \
|
||||
do {if (net_ratelimit()) EFX_ERR(efx, fmt, ##args); } while (0)
|
||||
|
||||
#define EFX_INFO_RL(efx, fmt, args...) \
|
||||
do {if (net_ratelimit()) EFX_INFO(efx, fmt, ##args); } while (0)
|
||||
|
||||
#define EFX_LOG_RL(efx, fmt, args...) \
|
||||
do {if (net_ratelimit()) EFX_LOG(efx, fmt, ##args); } while (0)
|
||||
|
||||
/* Kernel headers may redefine inline anyway */
|
||||
#ifndef inline
|
||||
#define inline inline __attribute__ ((always_inline))
|
||||
#endif
|
||||
|
||||
/**************************************************************************
|
||||
*
|
||||
* Efx data structures
|
||||
*
|
||||
**************************************************************************/
|
||||
|
||||
#define EFX_MAX_CHANNELS 32
|
||||
#define EFX_MAX_TX_QUEUES 1
|
||||
#define EFX_MAX_RX_QUEUES EFX_MAX_CHANNELS
|
||||
|
||||
/**
|
||||
* struct efx_special_buffer - An Efx special buffer
|
||||
* @addr: CPU base address of the buffer
|
||||
* @dma_addr: DMA base address of the buffer
|
||||
* @len: Buffer length, in bytes
|
||||
* @index: Buffer index within controller;s buffer table
|
||||
* @entries: Number of buffer table entries
|
||||
*
|
||||
* Special buffers are used for the event queues and the TX and RX
|
||||
* descriptor queues for each channel. They are *not* used for the
|
||||
* actual transmit and receive buffers.
|
||||
*
|
||||
* Note that for Falcon, TX and RX descriptor queues live in host memory.
|
||||
* Allocation and freeing procedures must take this into account.
|
||||
*/
|
||||
struct efx_special_buffer {
|
||||
void *addr;
|
||||
dma_addr_t dma_addr;
|
||||
unsigned int len;
|
||||
int index;
|
||||
int entries;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct efx_tx_buffer - An Efx TX buffer
|
||||
* @skb: The associated socket buffer.
|
||||
* Set only on the final fragment of a packet; %NULL for all other
|
||||
* fragments. When this fragment completes, then we can free this
|
||||
* skb.
|
||||
* @dma_addr: DMA address of the fragment.
|
||||
* @len: Length of this fragment.
|
||||
* This field is zero when the queue slot is empty.
|
||||
* @continuation: True if this fragment is not the end of a packet.
|
||||
* @unmap_single: True if pci_unmap_single should be used.
|
||||
* @unmap_addr: DMA address to unmap
|
||||
* @unmap_len: Length of this fragment to unmap
|
||||
*/
|
||||
struct efx_tx_buffer {
|
||||
const struct sk_buff *skb;
|
||||
dma_addr_t dma_addr;
|
||||
unsigned short len;
|
||||
unsigned char continuation;
|
||||
unsigned char unmap_single;
|
||||
dma_addr_t unmap_addr;
|
||||
unsigned short unmap_len;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct efx_tx_queue - An Efx TX queue
|
||||
*
|
||||
* This is a ring buffer of TX fragments.
|
||||
* Since the TX completion path always executes on the same
|
||||
* CPU and the xmit path can operate on different CPUs,
|
||||
* performance is increased by ensuring that the completion
|
||||
* path and the xmit path operate on different cache lines.
|
||||
* This is particularly important if the xmit path is always
|
||||
* executing on one CPU which is different from the completion
|
||||
* path. There is also a cache line for members which are
|
||||
* read but not written on the fast path.
|
||||
*
|
||||
* @efx: The associated Efx NIC
|
||||
* @queue: DMA queue number
|
||||
* @used: Queue is used by net driver
|
||||
* @channel: The associated channel
|
||||
* @buffer: The software buffer ring
|
||||
* @txd: The hardware descriptor ring
|
||||
* @read_count: Current read pointer.
|
||||
* This is the number of buffers that have been removed from both rings.
|
||||
* @stopped: Stopped flag.
|
||||
* Set if this TX queue is currently stopping its port.
|
||||
* @insert_count: Current insert pointer
|
||||
* This is the number of buffers that have been added to the
|
||||
* software ring.
|
||||
* @write_count: Current write pointer
|
||||
* This is the number of buffers that have been added to the
|
||||
* hardware ring.
|
||||
* @old_read_count: The value of read_count when last checked.
|
||||
* This is here for performance reasons. The xmit path will
|
||||
* only get the up-to-date value of read_count if this
|
||||
* variable indicates that the queue is full. This is to
|
||||
* avoid cache-line ping-pong between the xmit path and the
|
||||
* completion path.
|
||||
*/
|
||||
struct efx_tx_queue {
|
||||
/* Members which don't change on the fast path */
|
||||
struct efx_nic *efx ____cacheline_aligned_in_smp;
|
||||
int queue;
|
||||
int used;
|
||||
struct efx_channel *channel;
|
||||
struct efx_nic *nic;
|
||||
struct efx_tx_buffer *buffer;
|
||||
struct efx_special_buffer txd;
|
||||
|
||||
/* Members used mainly on the completion path */
|
||||
unsigned int read_count ____cacheline_aligned_in_smp;
|
||||
int stopped;
|
||||
|
||||
/* Members used only on the xmit path */
|
||||
unsigned int insert_count ____cacheline_aligned_in_smp;
|
||||
unsigned int write_count;
|
||||
unsigned int old_read_count;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct efx_rx_buffer - An Efx RX data buffer
|
||||
* @dma_addr: DMA base address of the buffer
|
||||
* @skb: The associated socket buffer, if any.
|
||||
* If both this and page are %NULL, the buffer slot is currently free.
|
||||
* @page: The associated page buffer, if any.
|
||||
* If both this and skb are %NULL, the buffer slot is currently free.
|
||||
* @data: Pointer to ethernet header
|
||||
* @len: Buffer length, in bytes.
|
||||
* @unmap_addr: DMA address to unmap
|
||||
*/
|
||||
struct efx_rx_buffer {
|
||||
dma_addr_t dma_addr;
|
||||
struct sk_buff *skb;
|
||||
struct page *page;
|
||||
char *data;
|
||||
unsigned int len;
|
||||
dma_addr_t unmap_addr;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct efx_rx_queue - An Efx RX queue
|
||||
* @efx: The associated Efx NIC
|
||||
* @queue: DMA queue number
|
||||
* @used: Queue is used by net driver
|
||||
* @channel: The associated channel
|
||||
* @buffer: The software buffer ring
|
||||
* @rxd: The hardware descriptor ring
|
||||
* @added_count: Number of buffers added to the receive queue.
|
||||
* @notified_count: Number of buffers given to NIC (<= @added_count).
|
||||
* @removed_count: Number of buffers removed from the receive queue.
|
||||
* @add_lock: Receive queue descriptor add spin lock.
|
||||
* This lock must be held in order to add buffers to the RX
|
||||
* descriptor ring (rxd and buffer) and to update added_count (but
|
||||
* not removed_count).
|
||||
* @max_fill: RX descriptor maximum fill level (<= ring size)
|
||||
* @fast_fill_trigger: RX descriptor fill level that will trigger a fast fill
|
||||
* (<= @max_fill)
|
||||
* @fast_fill_limit: The level to which a fast fill will fill
|
||||
* (@fast_fill_trigger <= @fast_fill_limit <= @max_fill)
|
||||
* @min_fill: RX descriptor minimum non-zero fill level.
|
||||
* This records the minimum fill level observed when a ring
|
||||
* refill was triggered.
|
||||
* @min_overfill: RX descriptor minimum overflow fill level.
|
||||
* This records the minimum fill level at which RX queue
|
||||
* overflow was observed. It should never be set.
|
||||
* @alloc_page_count: RX allocation strategy counter.
|
||||
* @alloc_skb_count: RX allocation strategy counter.
|
||||
* @work: Descriptor push work thread
|
||||
* @buf_page: Page for next RX buffer.
|
||||
* We can use a single page for multiple RX buffers. This tracks
|
||||
* the remaining space in the allocation.
|
||||
* @buf_dma_addr: Page's DMA address.
|
||||
* @buf_data: Page's host address.
|
||||
*/
|
||||
struct efx_rx_queue {
|
||||
struct efx_nic *efx;
|
||||
int queue;
|
||||
int used;
|
||||
struct efx_channel *channel;
|
||||
struct efx_rx_buffer *buffer;
|
||||
struct efx_special_buffer rxd;
|
||||
|
||||
int added_count;
|
||||
int notified_count;
|
||||
int removed_count;
|
||||
spinlock_t add_lock;
|
||||
unsigned int max_fill;
|
||||
unsigned int fast_fill_trigger;
|
||||
unsigned int fast_fill_limit;
|
||||
unsigned int min_fill;
|
||||
unsigned int min_overfill;
|
||||
unsigned int alloc_page_count;
|
||||
unsigned int alloc_skb_count;
|
||||
struct delayed_work work;
|
||||
unsigned int slow_fill_count;
|
||||
|
||||
struct page *buf_page;
|
||||
dma_addr_t buf_dma_addr;
|
||||
char *buf_data;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct efx_buffer - An Efx general-purpose buffer
|
||||
* @addr: host base address of the buffer
|
||||
* @dma_addr: DMA base address of the buffer
|
||||
* @len: Buffer length, in bytes
|
||||
*
|
||||
* Falcon uses these buffers for its interrupt status registers and
|
||||
* MAC stats dumps.
|
||||
*/
|
||||
struct efx_buffer {
|
||||
void *addr;
|
||||
dma_addr_t dma_addr;
|
||||
unsigned int len;
|
||||
};
|
||||
|
||||
|
||||
/* Flags for channel->used_flags */
|
||||
#define EFX_USED_BY_RX 1
|
||||
#define EFX_USED_BY_TX 2
|
||||
#define EFX_USED_BY_RX_TX (EFX_USED_BY_RX | EFX_USED_BY_TX)
|
||||
|
||||
enum efx_rx_alloc_method {
|
||||
RX_ALLOC_METHOD_AUTO = 0,
|
||||
RX_ALLOC_METHOD_SKB = 1,
|
||||
RX_ALLOC_METHOD_PAGE = 2,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct efx_channel - An Efx channel
|
||||
*
|
||||
* A channel comprises an event queue, at least one TX queue, at least
|
||||
* one RX queue, and an associated tasklet for processing the event
|
||||
* queue.
|
||||
*
|
||||
* @efx: Associated Efx NIC
|
||||
* @evqnum: Event queue number
|
||||
* @channel: Channel instance number
|
||||
* @used_flags: Channel is used by net driver
|
||||
* @enabled: Channel enabled indicator
|
||||
* @irq: IRQ number (MSI and MSI-X only)
|
||||
* @has_interrupt: Channel has an interrupt
|
||||
* @irq_moderation: IRQ moderation value (in us)
|
||||
* @napi_dev: Net device used with NAPI
|
||||
* @napi_str: NAPI control structure
|
||||
* @reset_work: Scheduled reset work thread
|
||||
* @work_pending: Is work pending via NAPI?
|
||||
* @eventq: Event queue buffer
|
||||
* @eventq_read_ptr: Event queue read pointer
|
||||
* @last_eventq_read_ptr: Last event queue read pointer value.
|
||||
* @eventq_magic: Event queue magic value for driver-generated test events
|
||||
* @lro_mgr: LRO state
|
||||
* @rx_alloc_level: Watermark based heuristic counter for pushing descriptors
|
||||
* and diagnostic counters
|
||||
* @rx_alloc_push_pages: RX allocation method currently in use for pushing
|
||||
* descriptors
|
||||
* @rx_alloc_pop_pages: RX allocation method currently in use for popping
|
||||
* descriptors
|
||||
* @n_rx_tobe_disc: Count of RX_TOBE_DISC errors
|
||||
* @n_rx_ip_frag_err: Count of RX IP fragment errors
|
||||
* @n_rx_ip_hdr_chksum_err: Count of RX IP header checksum errors
|
||||
* @n_rx_tcp_udp_chksum_err: Count of RX TCP and UDP checksum errors
|
||||
* @n_rx_frm_trunc: Count of RX_FRM_TRUNC errors
|
||||
* @n_rx_overlength: Count of RX_OVERLENGTH errors
|
||||
* @n_skbuff_leaks: Count of skbuffs leaked due to RX overrun
|
||||
*/
|
||||
struct efx_channel {
|
||||
struct efx_nic *efx;
|
||||
int evqnum;
|
||||
int channel;
|
||||
int used_flags;
|
||||
int enabled;
|
||||
int irq;
|
||||
unsigned int has_interrupt;
|
||||
unsigned int irq_moderation;
|
||||
struct net_device *napi_dev;
|
||||
struct napi_struct napi_str;
|
||||
struct work_struct reset_work;
|
||||
int work_pending;
|
||||
struct efx_special_buffer eventq;
|
||||
unsigned int eventq_read_ptr;
|
||||
unsigned int last_eventq_read_ptr;
|
||||
unsigned int eventq_magic;
|
||||
|
||||
struct net_lro_mgr lro_mgr;
|
||||
int rx_alloc_level;
|
||||
int rx_alloc_push_pages;
|
||||
int rx_alloc_pop_pages;
|
||||
|
||||
unsigned n_rx_tobe_disc;
|
||||
unsigned n_rx_ip_frag_err;
|
||||
unsigned n_rx_ip_hdr_chksum_err;
|
||||
unsigned n_rx_tcp_udp_chksum_err;
|
||||
unsigned n_rx_frm_trunc;
|
||||
unsigned n_rx_overlength;
|
||||
unsigned n_skbuff_leaks;
|
||||
|
||||
/* Used to pipeline received packets in order to optimise memory
|
||||
* access with prefetches.
|
||||
*/
|
||||
struct efx_rx_buffer *rx_pkt;
|
||||
int rx_pkt_csummed;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* struct efx_blinker - S/W LED blinking context
|
||||
* @led_num: LED ID (board-specific meaning)
|
||||
* @state: Current state - on or off
|
||||
* @resubmit: Timer resubmission flag
|
||||
* @timer: Control timer for blinking
|
||||
*/
|
||||
struct efx_blinker {
|
||||
int led_num;
|
||||
int state;
|
||||
int resubmit;
|
||||
struct timer_list timer;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* struct efx_board - board information
|
||||
* @type: Board model type
|
||||
* @major: Major rev. ('A', 'B' ...)
|
||||
* @minor: Minor rev. (0, 1, ...)
|
||||
* @init: Initialisation function
|
||||
* @init_leds: Sets up board LEDs
|
||||
* @set_fault_led: Turns the fault LED on or off
|
||||
* @blink: Starts/stops blinking
|
||||
* @blinker: used to blink LEDs in software
|
||||
*/
|
||||
struct efx_board {
|
||||
int type;
|
||||
int major;
|
||||
int minor;
|
||||
int (*init) (struct efx_nic *nic);
|
||||
/* As the LEDs are typically attached to the PHY, LEDs
|
||||
* have a separate init callback that happens later than
|
||||
* board init. */
|
||||
int (*init_leds)(struct efx_nic *efx);
|
||||
void (*set_fault_led) (struct efx_nic *efx, int state);
|
||||
void (*blink) (struct efx_nic *efx, int start);
|
||||
struct efx_blinker blinker;
|
||||
};
|
||||
|
||||
enum efx_int_mode {
|
||||
/* Be careful if altering to correct macro below */
|
||||
EFX_INT_MODE_MSIX = 0,
|
||||
EFX_INT_MODE_MSI = 1,
|
||||
EFX_INT_MODE_LEGACY = 2,
|
||||
EFX_INT_MODE_MAX /* Insert any new items before this */
|
||||
};
|
||||
#define EFX_INT_MODE_USE_MSI(x) (((x)->interrupt_mode) <= EFX_INT_MODE_MSI)
|
||||
|
||||
enum phy_type {
|
||||
PHY_TYPE_NONE = 0,
|
||||
PHY_TYPE_CX4_RTMR = 1,
|
||||
PHY_TYPE_1G_ALASKA = 2,
|
||||
PHY_TYPE_10XPRESS = 3,
|
||||
PHY_TYPE_XFP = 4,
|
||||
PHY_TYPE_PM8358 = 6,
|
||||
PHY_TYPE_MAX /* Insert any new items before this */
|
||||
};
|
||||
|
||||
#define PHY_ADDR_INVALID 0xff
|
||||
|
||||
enum nic_state {
|
||||
STATE_INIT = 0,
|
||||
STATE_RUNNING = 1,
|
||||
STATE_FINI = 2,
|
||||
STATE_RESETTING = 3, /* rtnl_lock always held */
|
||||
STATE_DISABLED = 4,
|
||||
STATE_MAX,
|
||||
};
|
||||
|
||||
/*
|
||||
* Alignment of page-allocated RX buffers
|
||||
*
|
||||
* Controls the number of bytes inserted at the start of an RX buffer.
|
||||
* This is the equivalent of NET_IP_ALIGN [which controls the alignment
|
||||
* of the skb->head for hardware DMA].
|
||||
*/
|
||||
#if defined(__i386__) || defined(__x86_64__)
|
||||
#define EFX_PAGE_IP_ALIGN 0
|
||||
#else
|
||||
#define EFX_PAGE_IP_ALIGN NET_IP_ALIGN
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Alignment of the skb->head which wraps a page-allocated RX buffer
|
||||
*
|
||||
* The skb allocated to wrap an rx_buffer can have this alignment. Since
|
||||
* the data is memcpy'd from the rx_buf, it does not need to be equal to
|
||||
* EFX_PAGE_IP_ALIGN.
|
||||
*/
|
||||
#define EFX_PAGE_SKB_ALIGN 2
|
||||
|
||||
/* Forward declaration */
|
||||
struct efx_nic;
|
||||
|
||||
/* Pseudo bit-mask flow control field */
|
||||
enum efx_fc_type {
|
||||
EFX_FC_RX = 1,
|
||||
EFX_FC_TX = 2,
|
||||
EFX_FC_AUTO = 4,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct efx_phy_operations - Efx PHY operations table
|
||||
* @init: Initialise PHY
|
||||
* @fini: Shut down PHY
|
||||
* @reconfigure: Reconfigure PHY (e.g. for new link parameters)
|
||||
* @clear_interrupt: Clear down interrupt
|
||||
* @blink: Blink LEDs
|
||||
* @check_hw: Check hardware
|
||||
* @reset_xaui: Reset XAUI side of PHY for (software sequenced reset)
|
||||
* @mmds: MMD presence mask
|
||||
*/
|
||||
struct efx_phy_operations {
|
||||
int (*init) (struct efx_nic *efx);
|
||||
void (*fini) (struct efx_nic *efx);
|
||||
void (*reconfigure) (struct efx_nic *efx);
|
||||
void (*clear_interrupt) (struct efx_nic *efx);
|
||||
int (*check_hw) (struct efx_nic *efx);
|
||||
void (*reset_xaui) (struct efx_nic *efx);
|
||||
int mmds;
|
||||
};
|
||||
|
||||
/*
|
||||
* Efx extended statistics
|
||||
*
|
||||
* Not all statistics are provided by all supported MACs. The purpose
|
||||
* is this structure is to contain the raw statistics provided by each
|
||||
* MAC.
|
||||
*/
|
||||
struct efx_mac_stats {
|
||||
u64 tx_bytes;
|
||||
u64 tx_good_bytes;
|
||||
u64 tx_bad_bytes;
|
||||
unsigned long tx_packets;
|
||||
unsigned long tx_bad;
|
||||
unsigned long tx_pause;
|
||||
unsigned long tx_control;
|
||||
unsigned long tx_unicast;
|
||||
unsigned long tx_multicast;
|
||||
unsigned long tx_broadcast;
|
||||
unsigned long tx_lt64;
|
||||
unsigned long tx_64;
|
||||
unsigned long tx_65_to_127;
|
||||
unsigned long tx_128_to_255;
|
||||
unsigned long tx_256_to_511;
|
||||
unsigned long tx_512_to_1023;
|
||||
unsigned long tx_1024_to_15xx;
|
||||
unsigned long tx_15xx_to_jumbo;
|
||||
unsigned long tx_gtjumbo;
|
||||
unsigned long tx_collision;
|
||||
unsigned long tx_single_collision;
|
||||
unsigned long tx_multiple_collision;
|
||||
unsigned long tx_excessive_collision;
|
||||
unsigned long tx_deferred;
|
||||
unsigned long tx_late_collision;
|
||||
unsigned long tx_excessive_deferred;
|
||||
unsigned long tx_non_tcpudp;
|
||||
unsigned long tx_mac_src_error;
|
||||
unsigned long tx_ip_src_error;
|
||||
u64 rx_bytes;
|
||||
u64 rx_good_bytes;
|
||||
u64 rx_bad_bytes;
|
||||
unsigned long rx_packets;
|
||||
unsigned long rx_good;
|
||||
unsigned long rx_bad;
|
||||
unsigned long rx_pause;
|
||||
unsigned long rx_control;
|
||||
unsigned long rx_unicast;
|
||||
unsigned long rx_multicast;
|
||||
unsigned long rx_broadcast;
|
||||
unsigned long rx_lt64;
|
||||
unsigned long rx_64;
|
||||
unsigned long rx_65_to_127;
|
||||
unsigned long rx_128_to_255;
|
||||
unsigned long rx_256_to_511;
|
||||
unsigned long rx_512_to_1023;
|
||||
unsigned long rx_1024_to_15xx;
|
||||
unsigned long rx_15xx_to_jumbo;
|
||||
unsigned long rx_gtjumbo;
|
||||
unsigned long rx_bad_lt64;
|
||||
unsigned long rx_bad_64_to_15xx;
|
||||
unsigned long rx_bad_15xx_to_jumbo;
|
||||
unsigned long rx_bad_gtjumbo;
|
||||
unsigned long rx_overflow;
|
||||
unsigned long rx_missed;
|
||||
unsigned long rx_false_carrier;
|
||||
unsigned long rx_symbol_error;
|
||||
unsigned long rx_align_error;
|
||||
unsigned long rx_length_error;
|
||||
unsigned long rx_internal_error;
|
||||
unsigned long rx_good_lt64;
|
||||
};
|
||||
|
||||
/* Number of bits used in a multicast filter hash address */
|
||||
#define EFX_MCAST_HASH_BITS 8
|
||||
|
||||
/* Number of (single-bit) entries in a multicast filter hash */
|
||||
#define EFX_MCAST_HASH_ENTRIES (1 << EFX_MCAST_HASH_BITS)
|
||||
|
||||
/* An Efx multicast filter hash */
|
||||
union efx_multicast_hash {
|
||||
u8 byte[EFX_MCAST_HASH_ENTRIES / 8];
|
||||
efx_oword_t oword[EFX_MCAST_HASH_ENTRIES / sizeof(efx_oword_t) / 8];
|
||||
};
|
||||
|
||||
/**
|
||||
* struct efx_nic - an Efx NIC
|
||||
* @name: Device name (net device name or bus id before net device registered)
|
||||
* @pci_dev: The PCI device
|
||||
* @type: Controller type attributes
|
||||
* @legacy_irq: IRQ number
|
||||
* @workqueue: Workqueue for resets, port reconfigures and the HW monitor
|
||||
* @reset_work: Scheduled reset workitem
|
||||
* @monitor_work: Hardware monitor workitem
|
||||
* @membase_phys: Memory BAR value as physical address
|
||||
* @membase: Memory BAR value
|
||||
* @biu_lock: BIU (bus interface unit) lock
|
||||
* @interrupt_mode: Interrupt mode
|
||||
* @i2c: I2C interface
|
||||
* @board_info: Board-level information
|
||||
* @state: Device state flag. Serialised by the rtnl_lock.
|
||||
* @reset_pending: Pending reset method (normally RESET_TYPE_NONE)
|
||||
* @tx_queue: TX DMA queues
|
||||
* @rx_queue: RX DMA queues
|
||||
* @channel: Channels
|
||||
* @rss_queues: Number of RSS queues
|
||||
* @rx_buffer_len: RX buffer length
|
||||
* @rx_buffer_order: Order (log2) of number of pages for each RX buffer
|
||||
* @irq_status: Interrupt status buffer
|
||||
* @last_irq_cpu: Last CPU to handle interrupt.
|
||||
* This register is written with the SMP processor ID whenever an
|
||||
* interrupt is handled. It is used by falcon_test_interrupt()
|
||||
* to verify that an interrupt has occurred.
|
||||
* @n_rx_nodesc_drop_cnt: RX no descriptor drop count
|
||||
* @nic_data: Hardware dependant state
|
||||
* @mac_lock: MAC access lock. Protects @port_enabled, efx_monitor() and
|
||||
* efx_reconfigure_port()
|
||||
* @port_enabled: Port enabled indicator.
|
||||
* Serialises efx_stop_all(), efx_start_all() and efx_monitor() and
|
||||
* efx_reconfigure_work with kernel interfaces. Safe to read under any
|
||||
* one of the rtnl_lock, mac_lock, or netif_tx_lock, but all three must
|
||||
* be held to modify it.
|
||||
* @port_initialized: Port initialized?
|
||||
* @net_dev: Operating system network device. Consider holding the rtnl lock
|
||||
* @rx_checksum_enabled: RX checksumming enabled
|
||||
* @netif_stop_count: Port stop count
|
||||
* @netif_stop_lock: Port stop lock
|
||||
* @mac_stats: MAC statistics. These include all statistics the MACs
|
||||
* can provide. Generic code converts these into a standard
|
||||
* &struct net_device_stats.
|
||||
* @stats_buffer: DMA buffer for statistics
|
||||
* @stats_lock: Statistics update lock
|
||||
* @mac_address: Permanent MAC address
|
||||
* @phy_type: PHY type
|
||||
* @phy_lock: PHY access lock
|
||||
* @phy_op: PHY interface
|
||||
* @phy_data: PHY private data (including PHY-specific stats)
|
||||
* @mii: PHY interface
|
||||
* @phy_powered: PHY power state
|
||||
* @tx_disabled: PHY transmitter turned off
|
||||
* @link_up: Link status
|
||||
* @link_options: Link options (MII/GMII format)
|
||||
* @n_link_state_changes: Number of times the link has changed state
|
||||
* @promiscuous: Promiscuous flag. Protected by netif_tx_lock.
|
||||
* @multicast_hash: Multicast hash table
|
||||
* @flow_control: Flow control flags - separate RX/TX so can't use link_options
|
||||
* @reconfigure_work: work item for dealing with PHY events
|
||||
*
|
||||
* The @priv field of the corresponding &struct net_device points to
|
||||
* this.
|
||||
*/
|
||||
struct efx_nic {
|
||||
char name[IFNAMSIZ];
|
||||
struct pci_dev *pci_dev;
|
||||
const struct efx_nic_type *type;
|
||||
int legacy_irq;
|
||||
struct workqueue_struct *workqueue;
|
||||
struct work_struct reset_work;
|
||||
struct delayed_work monitor_work;
|
||||
unsigned long membase_phys;
|
||||
void __iomem *membase;
|
||||
spinlock_t biu_lock;
|
||||
enum efx_int_mode interrupt_mode;
|
||||
|
||||
struct efx_i2c_interface i2c;
|
||||
struct efx_board board_info;
|
||||
|
||||
enum nic_state state;
|
||||
enum reset_type reset_pending;
|
||||
|
||||
struct efx_tx_queue tx_queue[EFX_MAX_TX_QUEUES];
|
||||
struct efx_rx_queue rx_queue[EFX_MAX_RX_QUEUES];
|
||||
struct efx_channel channel[EFX_MAX_CHANNELS];
|
||||
|
||||
int rss_queues;
|
||||
unsigned int rx_buffer_len;
|
||||
unsigned int rx_buffer_order;
|
||||
|
||||
struct efx_buffer irq_status;
|
||||
volatile signed int last_irq_cpu;
|
||||
|
||||
unsigned n_rx_nodesc_drop_cnt;
|
||||
|
||||
void *nic_data;
|
||||
|
||||
struct mutex mac_lock;
|
||||
int port_enabled;
|
||||
|
||||
int port_initialized;
|
||||
struct net_device *net_dev;
|
||||
int rx_checksum_enabled;
|
||||
|
||||
atomic_t netif_stop_count;
|
||||
spinlock_t netif_stop_lock;
|
||||
|
||||
struct efx_mac_stats mac_stats;
|
||||
struct efx_buffer stats_buffer;
|
||||
spinlock_t stats_lock;
|
||||
|
||||
unsigned char mac_address[ETH_ALEN];
|
||||
|
||||
enum phy_type phy_type;
|
||||
spinlock_t phy_lock;
|
||||
struct efx_phy_operations *phy_op;
|
||||
void *phy_data;
|
||||
struct mii_if_info mii;
|
||||
|
||||
int link_up;
|
||||
unsigned int link_options;
|
||||
unsigned int n_link_state_changes;
|
||||
|
||||
int promiscuous;
|
||||
union efx_multicast_hash multicast_hash;
|
||||
enum efx_fc_type flow_control;
|
||||
struct work_struct reconfigure_work;
|
||||
|
||||
atomic_t rx_reset;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct efx_nic_type - Efx device type definition
|
||||
* @mem_bar: Memory BAR number
|
||||
* @mem_map_size: Memory BAR mapped size
|
||||
* @txd_ptr_tbl_base: TX descriptor ring base address
|
||||
* @rxd_ptr_tbl_base: RX descriptor ring base address
|
||||
* @buf_tbl_base: Buffer table base address
|
||||
* @evq_ptr_tbl_base: Event queue pointer table base address
|
||||
* @evq_rptr_tbl_base: Event queue read-pointer table base address
|
||||
* @txd_ring_mask: TX descriptor ring size - 1 (must be a power of two - 1)
|
||||
* @rxd_ring_mask: RX descriptor ring size - 1 (must be a power of two - 1)
|
||||
* @evq_size: Event queue size (must be a power of two)
|
||||
* @max_dma_mask: Maximum possible DMA mask
|
||||
* @tx_dma_mask: TX DMA mask
|
||||
* @bug5391_mask: Address mask for bug 5391 workaround
|
||||
* @rx_xoff_thresh: RX FIFO XOFF watermark (bytes)
|
||||
* @rx_xon_thresh: RX FIFO XON watermark (bytes)
|
||||
* @rx_buffer_padding: Padding added to each RX buffer
|
||||
* @max_interrupt_mode: Highest capability interrupt mode supported
|
||||
* from &enum efx_init_mode.
|
||||
* @phys_addr_channels: Number of channels with physically addressed
|
||||
* descriptors
|
||||
*/
|
||||
struct efx_nic_type {
|
||||
unsigned int mem_bar;
|
||||
unsigned int mem_map_size;
|
||||
unsigned int txd_ptr_tbl_base;
|
||||
unsigned int rxd_ptr_tbl_base;
|
||||
unsigned int buf_tbl_base;
|
||||
unsigned int evq_ptr_tbl_base;
|
||||
unsigned int evq_rptr_tbl_base;
|
||||
|
||||
unsigned int txd_ring_mask;
|
||||
unsigned int rxd_ring_mask;
|
||||
unsigned int evq_size;
|
||||
dma_addr_t max_dma_mask;
|
||||
unsigned int tx_dma_mask;
|
||||
unsigned bug5391_mask;
|
||||
|
||||
int rx_xoff_thresh;
|
||||
int rx_xon_thresh;
|
||||
unsigned int rx_buffer_padding;
|
||||
unsigned int max_interrupt_mode;
|
||||
unsigned int phys_addr_channels;
|
||||
};
|
||||
|
||||
/**************************************************************************
|
||||
*
|
||||
* Prototypes and inline functions
|
||||
*
|
||||
*************************************************************************/
|
||||
|
||||
/* Iterate over all used channels */
|
||||
#define efx_for_each_channel(_channel, _efx) \
|
||||
for (_channel = &_efx->channel[0]; \
|
||||
_channel < &_efx->channel[EFX_MAX_CHANNELS]; \
|
||||
_channel++) \
|
||||
if (!_channel->used_flags) \
|
||||
continue; \
|
||||
else
|
||||
|
||||
/* Iterate over all used channels with interrupts */
|
||||
#define efx_for_each_channel_with_interrupt(_channel, _efx) \
|
||||
for (_channel = &_efx->channel[0]; \
|
||||
_channel < &_efx->channel[EFX_MAX_CHANNELS]; \
|
||||
_channel++) \
|
||||
if (!(_channel->used_flags && _channel->has_interrupt)) \
|
||||
continue; \
|
||||
else
|
||||
|
||||
/* Iterate over all used TX queues */
|
||||
#define efx_for_each_tx_queue(_tx_queue, _efx) \
|
||||
for (_tx_queue = &_efx->tx_queue[0]; \
|
||||
_tx_queue < &_efx->tx_queue[EFX_MAX_TX_QUEUES]; \
|
||||
_tx_queue++) \
|
||||
if (!_tx_queue->used) \
|
||||
continue; \
|
||||
else
|
||||
|
||||
/* Iterate over all TX queues belonging to a channel */
|
||||
#define efx_for_each_channel_tx_queue(_tx_queue, _channel) \
|
||||
for (_tx_queue = &_channel->efx->tx_queue[0]; \
|
||||
_tx_queue < &_channel->efx->tx_queue[EFX_MAX_TX_QUEUES]; \
|
||||
_tx_queue++) \
|
||||
if ((!_tx_queue->used) || \
|
||||
(_tx_queue->channel != _channel)) \
|
||||
continue; \
|
||||
else
|
||||
|
||||
/* Iterate over all used RX queues */
|
||||
#define efx_for_each_rx_queue(_rx_queue, _efx) \
|
||||
for (_rx_queue = &_efx->rx_queue[0]; \
|
||||
_rx_queue < &_efx->rx_queue[EFX_MAX_RX_QUEUES]; \
|
||||
_rx_queue++) \
|
||||
if (!_rx_queue->used) \
|
||||
continue; \
|
||||
else
|
||||
|
||||
/* Iterate over all RX queues belonging to a channel */
|
||||
#define efx_for_each_channel_rx_queue(_rx_queue, _channel) \
|
||||
for (_rx_queue = &_channel->efx->rx_queue[0]; \
|
||||
_rx_queue < &_channel->efx->rx_queue[EFX_MAX_RX_QUEUES]; \
|
||||
_rx_queue++) \
|
||||
if ((!_rx_queue->used) || \
|
||||
(_rx_queue->channel != _channel)) \
|
||||
continue; \
|
||||
else
|
||||
|
||||
/* Returns a pointer to the specified receive buffer in the RX
|
||||
* descriptor queue.
|
||||
*/
|
||||
static inline struct efx_rx_buffer *efx_rx_buffer(struct efx_rx_queue *rx_queue,
|
||||
unsigned int index)
|
||||
{
|
||||
return (&rx_queue->buffer[index]);
|
||||
}
|
||||
|
||||
/* Set bit in a little-endian bitfield */
|
||||
static inline void set_bit_le(int nr, unsigned char *addr)
|
||||
{
|
||||
addr[nr / 8] |= (1 << (nr % 8));
|
||||
}
|
||||
|
||||
/* Clear bit in a little-endian bitfield */
|
||||
static inline void clear_bit_le(int nr, unsigned char *addr)
|
||||
{
|
||||
addr[nr / 8] &= ~(1 << (nr % 8));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* EFX_MAX_FRAME_LEN - calculate maximum frame length
|
||||
*
|
||||
* This calculates the maximum frame length that will be used for a
|
||||
* given MTU. The frame length will be equal to the MTU plus a
|
||||
* constant amount of header space and padding. This is the quantity
|
||||
* that the net driver will program into the MAC as the maximum frame
|
||||
* length.
|
||||
*
|
||||
* The 10G MAC used in Falcon requires 8-byte alignment on the frame
|
||||
* length, so we round up to the nearest 8.
|
||||
*/
|
||||
#define EFX_MAX_FRAME_LEN(mtu) \
|
||||
((((mtu) + ETH_HLEN + VLAN_HLEN + 4/* FCS */) + 7) & ~7)
|
||||
|
||||
|
||||
#endif /* EFX_NET_DRIVER_H */
|
48
drivers/net/sfc/phy.h
Normal file
48
drivers/net/sfc/phy.h
Normal file
@ -0,0 +1,48 @@
|
||||
/****************************************************************************
|
||||
* Driver for Solarflare Solarstorm network controllers and boards
|
||||
* Copyright 2007 Solarflare Communications Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published
|
||||
* by the Free Software Foundation, incorporated herein by reference.
|
||||
*/
|
||||
|
||||
#ifndef EFX_PHY_H
|
||||
#define EFX_PHY_H
|
||||
|
||||
/****************************************************************************
|
||||
* 10Xpress (SFX7101) PHY
|
||||
*/
|
||||
extern struct efx_phy_operations falcon_tenxpress_phy_ops;
|
||||
|
||||
enum tenxpress_state {
|
||||
TENXPRESS_STATUS_OFF = 0,
|
||||
TENXPRESS_STATUS_OTEMP = 1,
|
||||
TENXPRESS_STATUS_NORMAL = 2,
|
||||
};
|
||||
|
||||
extern void tenxpress_set_state(struct efx_nic *efx,
|
||||
enum tenxpress_state state);
|
||||
extern void tenxpress_phy_blink(struct efx_nic *efx, int blink);
|
||||
extern void tenxpress_crc_err(struct efx_nic *efx);
|
||||
|
||||
/****************************************************************************
|
||||
* Exported functions from the driver for XFP optical PHYs
|
||||
*/
|
||||
extern struct efx_phy_operations falcon_xfp_phy_ops;
|
||||
|
||||
/* The QUAKE XFP PHY provides various H/W control states for LEDs */
|
||||
#define QUAKE_LED_LINK_INVAL (0)
|
||||
#define QUAKE_LED_LINK_STAT (1)
|
||||
#define QUAKE_LED_LINK_ACT (2)
|
||||
#define QUAKE_LED_LINK_ACTSTAT (3)
|
||||
#define QUAKE_LED_OFF (4)
|
||||
#define QUAKE_LED_ON (5)
|
||||
#define QUAKE_LED_LINK_INPUT (6) /* Pin is an input. */
|
||||
/* What link the LED tracks */
|
||||
#define QUAKE_LED_TXLINK (0)
|
||||
#define QUAKE_LED_RXLINK (8)
|
||||
|
||||
extern void xfp_set_led(struct efx_nic *p, int led, int state);
|
||||
|
||||
#endif
|
875
drivers/net/sfc/rx.c
Normal file
875
drivers/net/sfc/rx.c
Normal file
@ -0,0 +1,875 @@
|
||||
/****************************************************************************
|
||||
* Driver for Solarflare Solarstorm network controllers and boards
|
||||
* Copyright 2005-2006 Fen Systems Ltd.
|
||||
* Copyright 2005-2008 Solarflare Communications Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published
|
||||
* by the Free Software Foundation, incorporated herein by reference.
|
||||
*/
|
||||
|
||||
#include <linux/socket.h>
|
||||
#include <linux/in.h>
|
||||
#include <linux/ip.h>
|
||||
#include <linux/tcp.h>
|
||||
#include <linux/udp.h>
|
||||
#include <net/ip.h>
|
||||
#include <net/checksum.h>
|
||||
#include "net_driver.h"
|
||||
#include "rx.h"
|
||||
#include "efx.h"
|
||||
#include "falcon.h"
|
||||
#include "workarounds.h"
|
||||
|
||||
/* Number of RX descriptors pushed at once. */
|
||||
#define EFX_RX_BATCH 8
|
||||
|
||||
/* Size of buffer allocated for skb header area. */
|
||||
#define EFX_SKB_HEADERS 64u
|
||||
|
||||
/*
|
||||
* rx_alloc_method - RX buffer allocation method
|
||||
*
|
||||
* This driver supports two methods for allocating and using RX buffers:
|
||||
* each RX buffer may be backed by an skb or by an order-n page.
|
||||
*
|
||||
* When LRO is in use then the second method has a lower overhead,
|
||||
* since we don't have to allocate then free skbs on reassembled frames.
|
||||
*
|
||||
* Values:
|
||||
* - RX_ALLOC_METHOD_AUTO = 0
|
||||
* - RX_ALLOC_METHOD_SKB = 1
|
||||
* - RX_ALLOC_METHOD_PAGE = 2
|
||||
*
|
||||
* The heuristic for %RX_ALLOC_METHOD_AUTO is a simple hysteresis count
|
||||
* controlled by the parameters below.
|
||||
*
|
||||
* - Since pushing and popping descriptors are separated by the rx_queue
|
||||
* size, so the watermarks should be ~rxd_size.
|
||||
* - The performance win by using page-based allocation for LRO is less
|
||||
* than the performance hit of using page-based allocation of non-LRO,
|
||||
* so the watermarks should reflect this.
|
||||
*
|
||||
* Per channel we maintain a single variable, updated by each channel:
|
||||
*
|
||||
* rx_alloc_level += (lro_performed ? RX_ALLOC_FACTOR_LRO :
|
||||
* RX_ALLOC_FACTOR_SKB)
|
||||
* Per NAPI poll interval, we constrain rx_alloc_level to 0..MAX (which
|
||||
* limits the hysteresis), and update the allocation strategy:
|
||||
*
|
||||
* rx_alloc_method = (rx_alloc_level > RX_ALLOC_LEVEL_LRO ?
|
||||
* RX_ALLOC_METHOD_PAGE : RX_ALLOC_METHOD_SKB)
|
||||
*/
|
||||
static int rx_alloc_method = RX_ALLOC_METHOD_PAGE;
|
||||
|
||||
#define RX_ALLOC_LEVEL_LRO 0x2000
|
||||
#define RX_ALLOC_LEVEL_MAX 0x3000
|
||||
#define RX_ALLOC_FACTOR_LRO 1
|
||||
#define RX_ALLOC_FACTOR_SKB (-2)
|
||||
|
||||
/* This is the percentage fill level below which new RX descriptors
|
||||
* will be added to the RX descriptor ring.
|
||||
*/
|
||||
static unsigned int rx_refill_threshold = 90;
|
||||
|
||||
/* This is the percentage fill level to which an RX queue will be refilled
|
||||
* when the "RX refill threshold" is reached.
|
||||
*/
|
||||
static unsigned int rx_refill_limit = 95;
|
||||
|
||||
/*
|
||||
* RX maximum head room required.
|
||||
*
|
||||
* This must be at least 1 to prevent overflow and at least 2 to allow
|
||||
* pipelined receives.
|
||||
*/
|
||||
#define EFX_RXD_HEAD_ROOM 2
|
||||
|
||||
/* Macros for zero-order pages (potentially) containing multiple RX buffers */
|
||||
#define RX_DATA_OFFSET(_data) \
|
||||
(((unsigned long) (_data)) & (PAGE_SIZE-1))
|
||||
#define RX_BUF_OFFSET(_rx_buf) \
|
||||
RX_DATA_OFFSET((_rx_buf)->data)
|
||||
|
||||
#define RX_PAGE_SIZE(_efx) \
|
||||
(PAGE_SIZE * (1u << (_efx)->rx_buffer_order))
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
*
|
||||
* Linux generic LRO handling
|
||||
*
|
||||
**************************************************************************
|
||||
*/
|
||||
|
||||
static int efx_lro_get_skb_hdr(struct sk_buff *skb, void **ip_hdr,
|
||||
void **tcpudp_hdr, u64 *hdr_flags, void *priv)
|
||||
{
|
||||
struct efx_channel *channel = (struct efx_channel *)priv;
|
||||
struct iphdr *iph;
|
||||
struct tcphdr *th;
|
||||
|
||||
iph = (struct iphdr *)skb->data;
|
||||
if (skb->protocol != htons(ETH_P_IP) || iph->protocol != IPPROTO_TCP)
|
||||
goto fail;
|
||||
|
||||
th = (struct tcphdr *)(skb->data + iph->ihl * 4);
|
||||
|
||||
*tcpudp_hdr = th;
|
||||
*ip_hdr = iph;
|
||||
*hdr_flags = LRO_IPV4 | LRO_TCP;
|
||||
|
||||
channel->rx_alloc_level += RX_ALLOC_FACTOR_LRO;
|
||||
return 0;
|
||||
fail:
|
||||
channel->rx_alloc_level += RX_ALLOC_FACTOR_SKB;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int efx_get_frag_hdr(struct skb_frag_struct *frag, void **mac_hdr,
|
||||
void **ip_hdr, void **tcpudp_hdr, u64 *hdr_flags,
|
||||
void *priv)
|
||||
{
|
||||
struct efx_channel *channel = (struct efx_channel *)priv;
|
||||
struct ethhdr *eh;
|
||||
struct iphdr *iph;
|
||||
|
||||
/* We support EtherII and VLAN encapsulated IPv4 */
|
||||
eh = (struct ethhdr *)(page_address(frag->page) + frag->page_offset);
|
||||
*mac_hdr = eh;
|
||||
|
||||
if (eh->h_proto == htons(ETH_P_IP)) {
|
||||
iph = (struct iphdr *)(eh + 1);
|
||||
} else {
|
||||
struct vlan_ethhdr *veh = (struct vlan_ethhdr *)eh;
|
||||
if (veh->h_vlan_encapsulated_proto != htons(ETH_P_IP))
|
||||
goto fail;
|
||||
|
||||
iph = (struct iphdr *)(veh + 1);
|
||||
}
|
||||
*ip_hdr = iph;
|
||||
|
||||
/* We can only do LRO over TCP */
|
||||
if (iph->protocol != IPPROTO_TCP)
|
||||
goto fail;
|
||||
|
||||
*hdr_flags = LRO_IPV4 | LRO_TCP;
|
||||
*tcpudp_hdr = (struct tcphdr *)((u8 *) iph + iph->ihl * 4);
|
||||
|
||||
channel->rx_alloc_level += RX_ALLOC_FACTOR_LRO;
|
||||
return 0;
|
||||
fail:
|
||||
channel->rx_alloc_level += RX_ALLOC_FACTOR_SKB;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int efx_lro_init(struct net_lro_mgr *lro_mgr, struct efx_nic *efx)
|
||||
{
|
||||
size_t s = sizeof(struct net_lro_desc) * EFX_MAX_LRO_DESCRIPTORS;
|
||||
struct net_lro_desc *lro_arr;
|
||||
|
||||
/* Allocate the LRO descriptors structure */
|
||||
lro_arr = kzalloc(s, GFP_KERNEL);
|
||||
if (lro_arr == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
lro_mgr->lro_arr = lro_arr;
|
||||
lro_mgr->max_desc = EFX_MAX_LRO_DESCRIPTORS;
|
||||
lro_mgr->max_aggr = EFX_MAX_LRO_AGGR;
|
||||
lro_mgr->frag_align_pad = EFX_PAGE_SKB_ALIGN;
|
||||
|
||||
lro_mgr->get_skb_header = efx_lro_get_skb_hdr;
|
||||
lro_mgr->get_frag_header = efx_get_frag_hdr;
|
||||
lro_mgr->dev = efx->net_dev;
|
||||
|
||||
lro_mgr->features = LRO_F_NAPI;
|
||||
|
||||
/* We can pass packets up with the checksum intact */
|
||||
lro_mgr->ip_summed = CHECKSUM_UNNECESSARY;
|
||||
|
||||
lro_mgr->ip_summed_aggr = CHECKSUM_UNNECESSARY;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void efx_lro_fini(struct net_lro_mgr *lro_mgr)
|
||||
{
|
||||
kfree(lro_mgr->lro_arr);
|
||||
lro_mgr->lro_arr = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* efx_init_rx_buffer_skb - create new RX buffer using skb-based allocation
|
||||
*
|
||||
* @rx_queue: Efx RX queue
|
||||
* @rx_buf: RX buffer structure to populate
|
||||
*
|
||||
* This allocates memory for a new receive buffer, maps it for DMA,
|
||||
* and populates a struct efx_rx_buffer with the relevant
|
||||
* information. Return a negative error code or 0 on success.
|
||||
*/
|
||||
static inline int efx_init_rx_buffer_skb(struct efx_rx_queue *rx_queue,
|
||||
struct efx_rx_buffer *rx_buf)
|
||||
{
|
||||
struct efx_nic *efx = rx_queue->efx;
|
||||
struct net_device *net_dev = efx->net_dev;
|
||||
int skb_len = efx->rx_buffer_len;
|
||||
|
||||
rx_buf->skb = netdev_alloc_skb(net_dev, skb_len);
|
||||
if (unlikely(!rx_buf->skb))
|
||||
return -ENOMEM;
|
||||
|
||||
/* Adjust the SKB for padding and checksum */
|
||||
skb_reserve(rx_buf->skb, NET_IP_ALIGN);
|
||||
rx_buf->len = skb_len - NET_IP_ALIGN;
|
||||
rx_buf->data = (char *)rx_buf->skb->data;
|
||||
rx_buf->skb->ip_summed = CHECKSUM_UNNECESSARY;
|
||||
|
||||
rx_buf->dma_addr = pci_map_single(efx->pci_dev,
|
||||
rx_buf->data, rx_buf->len,
|
||||
PCI_DMA_FROMDEVICE);
|
||||
|
||||
if (unlikely(pci_dma_mapping_error(rx_buf->dma_addr))) {
|
||||
dev_kfree_skb_any(rx_buf->skb);
|
||||
rx_buf->skb = NULL;
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* efx_init_rx_buffer_page - create new RX buffer using page-based allocation
|
||||
*
|
||||
* @rx_queue: Efx RX queue
|
||||
* @rx_buf: RX buffer structure to populate
|
||||
*
|
||||
* This allocates memory for a new receive buffer, maps it for DMA,
|
||||
* and populates a struct efx_rx_buffer with the relevant
|
||||
* information. Return a negative error code or 0 on success.
|
||||
*/
|
||||
static inline int efx_init_rx_buffer_page(struct efx_rx_queue *rx_queue,
|
||||
struct efx_rx_buffer *rx_buf)
|
||||
{
|
||||
struct efx_nic *efx = rx_queue->efx;
|
||||
int bytes, space, offset;
|
||||
|
||||
bytes = efx->rx_buffer_len - EFX_PAGE_IP_ALIGN;
|
||||
|
||||
/* If there is space left in the previously allocated page,
|
||||
* then use it. Otherwise allocate a new one */
|
||||
rx_buf->page = rx_queue->buf_page;
|
||||
if (rx_buf->page == NULL) {
|
||||
dma_addr_t dma_addr;
|
||||
|
||||
rx_buf->page = alloc_pages(__GFP_COLD | __GFP_COMP | GFP_ATOMIC,
|
||||
efx->rx_buffer_order);
|
||||
if (unlikely(rx_buf->page == NULL))
|
||||
return -ENOMEM;
|
||||
|
||||
dma_addr = pci_map_page(efx->pci_dev, rx_buf->page,
|
||||
0, RX_PAGE_SIZE(efx),
|
||||
PCI_DMA_FROMDEVICE);
|
||||
|
||||
if (unlikely(pci_dma_mapping_error(dma_addr))) {
|
||||
__free_pages(rx_buf->page, efx->rx_buffer_order);
|
||||
rx_buf->page = NULL;
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
rx_queue->buf_page = rx_buf->page;
|
||||
rx_queue->buf_dma_addr = dma_addr;
|
||||
rx_queue->buf_data = ((char *) page_address(rx_buf->page) +
|
||||
EFX_PAGE_IP_ALIGN);
|
||||
}
|
||||
|
||||
offset = RX_DATA_OFFSET(rx_queue->buf_data);
|
||||
rx_buf->len = bytes;
|
||||
rx_buf->dma_addr = rx_queue->buf_dma_addr + offset;
|
||||
rx_buf->data = rx_queue->buf_data;
|
||||
|
||||
/* Try to pack multiple buffers per page */
|
||||
if (efx->rx_buffer_order == 0) {
|
||||
/* The next buffer starts on the next 512 byte boundary */
|
||||
rx_queue->buf_data += ((bytes + 0x1ff) & ~0x1ff);
|
||||
offset += ((bytes + 0x1ff) & ~0x1ff);
|
||||
|
||||
space = RX_PAGE_SIZE(efx) - offset;
|
||||
if (space >= bytes) {
|
||||
/* Refs dropped on kernel releasing each skb */
|
||||
get_page(rx_queue->buf_page);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
/* This is the final RX buffer for this page, so mark it for
|
||||
* unmapping */
|
||||
rx_queue->buf_page = NULL;
|
||||
rx_buf->unmap_addr = rx_queue->buf_dma_addr;
|
||||
|
||||
out:
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* This allocates memory for a new receive buffer, maps it for DMA,
|
||||
* and populates a struct efx_rx_buffer with the relevant
|
||||
* information.
|
||||
*/
|
||||
static inline int efx_init_rx_buffer(struct efx_rx_queue *rx_queue,
|
||||
struct efx_rx_buffer *new_rx_buf)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
if (rx_queue->channel->rx_alloc_push_pages) {
|
||||
new_rx_buf->skb = NULL;
|
||||
rc = efx_init_rx_buffer_page(rx_queue, new_rx_buf);
|
||||
rx_queue->alloc_page_count++;
|
||||
} else {
|
||||
new_rx_buf->page = NULL;
|
||||
rc = efx_init_rx_buffer_skb(rx_queue, new_rx_buf);
|
||||
rx_queue->alloc_skb_count++;
|
||||
}
|
||||
|
||||
if (unlikely(rc < 0))
|
||||
EFX_LOG_RL(rx_queue->efx, "%s RXQ[%d] =%d\n", __func__,
|
||||
rx_queue->queue, rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static inline void efx_unmap_rx_buffer(struct efx_nic *efx,
|
||||
struct efx_rx_buffer *rx_buf)
|
||||
{
|
||||
if (rx_buf->page) {
|
||||
EFX_BUG_ON_PARANOID(rx_buf->skb);
|
||||
if (rx_buf->unmap_addr) {
|
||||
pci_unmap_page(efx->pci_dev, rx_buf->unmap_addr,
|
||||
RX_PAGE_SIZE(efx), PCI_DMA_FROMDEVICE);
|
||||
rx_buf->unmap_addr = 0;
|
||||
}
|
||||
} else if (likely(rx_buf->skb)) {
|
||||
pci_unmap_single(efx->pci_dev, rx_buf->dma_addr,
|
||||
rx_buf->len, PCI_DMA_FROMDEVICE);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void efx_free_rx_buffer(struct efx_nic *efx,
|
||||
struct efx_rx_buffer *rx_buf)
|
||||
{
|
||||
if (rx_buf->page) {
|
||||
__free_pages(rx_buf->page, efx->rx_buffer_order);
|
||||
rx_buf->page = NULL;
|
||||
} else if (likely(rx_buf->skb)) {
|
||||
dev_kfree_skb_any(rx_buf->skb);
|
||||
rx_buf->skb = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void efx_fini_rx_buffer(struct efx_rx_queue *rx_queue,
|
||||
struct efx_rx_buffer *rx_buf)
|
||||
{
|
||||
efx_unmap_rx_buffer(rx_queue->efx, rx_buf);
|
||||
efx_free_rx_buffer(rx_queue->efx, rx_buf);
|
||||
}
|
||||
|
||||
/**
|
||||
* efx_fast_push_rx_descriptors - push new RX descriptors quickly
|
||||
* @rx_queue: RX descriptor queue
|
||||
* @retry: Recheck the fill level
|
||||
* This will aim to fill the RX descriptor queue up to
|
||||
* @rx_queue->@fast_fill_limit. If there is insufficient atomic
|
||||
* memory to do so, the caller should retry.
|
||||
*/
|
||||
static int __efx_fast_push_rx_descriptors(struct efx_rx_queue *rx_queue,
|
||||
int retry)
|
||||
{
|
||||
struct efx_rx_buffer *rx_buf;
|
||||
unsigned fill_level, index;
|
||||
int i, space, rc = 0;
|
||||
|
||||
/* Calculate current fill level. Do this outside the lock,
|
||||
* because most of the time we'll end up not wanting to do the
|
||||
* fill anyway.
|
||||
*/
|
||||
fill_level = (rx_queue->added_count - rx_queue->removed_count);
|
||||
EFX_BUG_ON_PARANOID(fill_level >
|
||||
rx_queue->efx->type->rxd_ring_mask + 1);
|
||||
|
||||
/* Don't fill if we don't need to */
|
||||
if (fill_level >= rx_queue->fast_fill_trigger)
|
||||
return 0;
|
||||
|
||||
/* Record minimum fill level */
|
||||
if (unlikely(fill_level < rx_queue->min_fill))
|
||||
if (fill_level)
|
||||
rx_queue->min_fill = fill_level;
|
||||
|
||||
/* Acquire RX add lock. If this lock is contended, then a fast
|
||||
* fill must already be in progress (e.g. in the refill
|
||||
* tasklet), so we don't need to do anything
|
||||
*/
|
||||
if (!spin_trylock_bh(&rx_queue->add_lock))
|
||||
return -1;
|
||||
|
||||
retry:
|
||||
/* Recalculate current fill level now that we have the lock */
|
||||
fill_level = (rx_queue->added_count - rx_queue->removed_count);
|
||||
EFX_BUG_ON_PARANOID(fill_level >
|
||||
rx_queue->efx->type->rxd_ring_mask + 1);
|
||||
space = rx_queue->fast_fill_limit - fill_level;
|
||||
if (space < EFX_RX_BATCH)
|
||||
goto out_unlock;
|
||||
|
||||
EFX_TRACE(rx_queue->efx, "RX queue %d fast-filling descriptor ring from"
|
||||
" level %d to level %d using %s allocation\n",
|
||||
rx_queue->queue, fill_level, rx_queue->fast_fill_limit,
|
||||
rx_queue->channel->rx_alloc_push_pages ? "page" : "skb");
|
||||
|
||||
do {
|
||||
for (i = 0; i < EFX_RX_BATCH; ++i) {
|
||||
index = (rx_queue->added_count &
|
||||
rx_queue->efx->type->rxd_ring_mask);
|
||||
rx_buf = efx_rx_buffer(rx_queue, index);
|
||||
rc = efx_init_rx_buffer(rx_queue, rx_buf);
|
||||
if (unlikely(rc))
|
||||
goto out;
|
||||
++rx_queue->added_count;
|
||||
}
|
||||
} while ((space -= EFX_RX_BATCH) >= EFX_RX_BATCH);
|
||||
|
||||
EFX_TRACE(rx_queue->efx, "RX queue %d fast-filled descriptor ring "
|
||||
"to level %d\n", rx_queue->queue,
|
||||
rx_queue->added_count - rx_queue->removed_count);
|
||||
|
||||
out:
|
||||
/* Send write pointer to card. */
|
||||
falcon_notify_rx_desc(rx_queue);
|
||||
|
||||
/* If the fast fill is running inside from the refill tasklet, then
|
||||
* for SMP systems it may be running on a different CPU to
|
||||
* RX event processing, which means that the fill level may now be
|
||||
* out of date. */
|
||||
if (unlikely(retry && (rc == 0)))
|
||||
goto retry;
|
||||
|
||||
out_unlock:
|
||||
spin_unlock_bh(&rx_queue->add_lock);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* efx_fast_push_rx_descriptors - push new RX descriptors quickly
|
||||
* @rx_queue: RX descriptor queue
|
||||
*
|
||||
* This will aim to fill the RX descriptor queue up to
|
||||
* @rx_queue->@fast_fill_limit. If there is insufficient memory to do so,
|
||||
* it will schedule a work item to immediately continue the fast fill
|
||||
*/
|
||||
void efx_fast_push_rx_descriptors(struct efx_rx_queue *rx_queue)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = __efx_fast_push_rx_descriptors(rx_queue, 0);
|
||||
if (unlikely(rc)) {
|
||||
/* Schedule the work item to run immediately. The hope is
|
||||
* that work is immediately pending to free some memory
|
||||
* (e.g. an RX event or TX completion)
|
||||
*/
|
||||
efx_schedule_slow_fill(rx_queue, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void efx_rx_work(struct work_struct *data)
|
||||
{
|
||||
struct efx_rx_queue *rx_queue;
|
||||
int rc;
|
||||
|
||||
rx_queue = container_of(data, struct efx_rx_queue, work.work);
|
||||
|
||||
if (unlikely(!rx_queue->channel->enabled))
|
||||
return;
|
||||
|
||||
EFX_TRACE(rx_queue->efx, "RX queue %d worker thread executing on CPU "
|
||||
"%d\n", rx_queue->queue, raw_smp_processor_id());
|
||||
|
||||
++rx_queue->slow_fill_count;
|
||||
/* Push new RX descriptors, allowing at least 1 jiffy for
|
||||
* the kernel to free some more memory. */
|
||||
rc = __efx_fast_push_rx_descriptors(rx_queue, 1);
|
||||
if (rc)
|
||||
efx_schedule_slow_fill(rx_queue, 1);
|
||||
}
|
||||
|
||||
static inline void efx_rx_packet__check_len(struct efx_rx_queue *rx_queue,
|
||||
struct efx_rx_buffer *rx_buf,
|
||||
int len, int *discard,
|
||||
int *leak_packet)
|
||||
{
|
||||
struct efx_nic *efx = rx_queue->efx;
|
||||
unsigned max_len = rx_buf->len - efx->type->rx_buffer_padding;
|
||||
|
||||
if (likely(len <= max_len))
|
||||
return;
|
||||
|
||||
/* The packet must be discarded, but this is only a fatal error
|
||||
* if the caller indicated it was
|
||||
*/
|
||||
*discard = 1;
|
||||
|
||||
if ((len > rx_buf->len) && EFX_WORKAROUND_8071(efx)) {
|
||||
EFX_ERR_RL(efx, " RX queue %d seriously overlength "
|
||||
"RX event (0x%x > 0x%x+0x%x). Leaking\n",
|
||||
rx_queue->queue, len, max_len,
|
||||
efx->type->rx_buffer_padding);
|
||||
/* If this buffer was skb-allocated, then the meta
|
||||
* data at the end of the skb will be trashed. So
|
||||
* we have no choice but to leak the fragment.
|
||||
*/
|
||||
*leak_packet = (rx_buf->skb != NULL);
|
||||
efx_schedule_reset(efx, RESET_TYPE_RX_RECOVERY);
|
||||
} else {
|
||||
EFX_ERR_RL(efx, " RX queue %d overlength RX event "
|
||||
"(0x%x > 0x%x)\n", rx_queue->queue, len, max_len);
|
||||
}
|
||||
|
||||
rx_queue->channel->n_rx_overlength++;
|
||||
}
|
||||
|
||||
/* Pass a received packet up through the generic LRO stack
|
||||
*
|
||||
* Handles driverlink veto, and passes the fragment up via
|
||||
* the appropriate LRO method
|
||||
*/
|
||||
static inline void efx_rx_packet_lro(struct efx_channel *channel,
|
||||
struct efx_rx_buffer *rx_buf)
|
||||
{
|
||||
struct net_lro_mgr *lro_mgr = &channel->lro_mgr;
|
||||
void *priv = channel;
|
||||
|
||||
/* Pass the skb/page into the LRO engine */
|
||||
if (rx_buf->page) {
|
||||
struct skb_frag_struct frags;
|
||||
|
||||
frags.page = rx_buf->page;
|
||||
frags.page_offset = RX_BUF_OFFSET(rx_buf);
|
||||
frags.size = rx_buf->len;
|
||||
|
||||
lro_receive_frags(lro_mgr, &frags, rx_buf->len,
|
||||
rx_buf->len, priv, 0);
|
||||
|
||||
EFX_BUG_ON_PARANOID(rx_buf->skb);
|
||||
rx_buf->page = NULL;
|
||||
} else {
|
||||
EFX_BUG_ON_PARANOID(!rx_buf->skb);
|
||||
|
||||
lro_receive_skb(lro_mgr, rx_buf->skb, priv);
|
||||
rx_buf->skb = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Allocate and construct an SKB around a struct page.*/
|
||||
static inline struct sk_buff *efx_rx_mk_skb(struct efx_rx_buffer *rx_buf,
|
||||
struct efx_nic *efx,
|
||||
int hdr_len)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
|
||||
/* Allocate an SKB to store the headers */
|
||||
skb = netdev_alloc_skb(efx->net_dev, hdr_len + EFX_PAGE_SKB_ALIGN);
|
||||
if (unlikely(skb == NULL)) {
|
||||
EFX_ERR_RL(efx, "RX out of memory for skb\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
EFX_BUG_ON_PARANOID(skb_shinfo(skb)->nr_frags);
|
||||
EFX_BUG_ON_PARANOID(rx_buf->len < hdr_len);
|
||||
|
||||
skb->ip_summed = CHECKSUM_UNNECESSARY;
|
||||
skb_reserve(skb, EFX_PAGE_SKB_ALIGN);
|
||||
|
||||
skb->len = rx_buf->len;
|
||||
skb->truesize = rx_buf->len + sizeof(struct sk_buff);
|
||||
memcpy(skb->data, rx_buf->data, hdr_len);
|
||||
skb->tail += hdr_len;
|
||||
|
||||
/* Append the remaining page onto the frag list */
|
||||
if (unlikely(rx_buf->len > hdr_len)) {
|
||||
struct skb_frag_struct *frag = skb_shinfo(skb)->frags;
|
||||
frag->page = rx_buf->page;
|
||||
frag->page_offset = RX_BUF_OFFSET(rx_buf) + hdr_len;
|
||||
frag->size = skb->len - hdr_len;
|
||||
skb_shinfo(skb)->nr_frags = 1;
|
||||
skb->data_len = frag->size;
|
||||
} else {
|
||||
__free_pages(rx_buf->page, efx->rx_buffer_order);
|
||||
skb->data_len = 0;
|
||||
}
|
||||
|
||||
/* Ownership has transferred from the rx_buf to skb */
|
||||
rx_buf->page = NULL;
|
||||
|
||||
/* Move past the ethernet header */
|
||||
skb->protocol = eth_type_trans(skb, efx->net_dev);
|
||||
|
||||
return skb;
|
||||
}
|
||||
|
||||
void efx_rx_packet(struct efx_rx_queue *rx_queue, unsigned int index,
|
||||
unsigned int len, int checksummed, int discard)
|
||||
{
|
||||
struct efx_nic *efx = rx_queue->efx;
|
||||
struct efx_rx_buffer *rx_buf;
|
||||
int leak_packet = 0;
|
||||
|
||||
rx_buf = efx_rx_buffer(rx_queue, index);
|
||||
EFX_BUG_ON_PARANOID(!rx_buf->data);
|
||||
EFX_BUG_ON_PARANOID(rx_buf->skb && rx_buf->page);
|
||||
EFX_BUG_ON_PARANOID(!(rx_buf->skb || rx_buf->page));
|
||||
|
||||
/* This allows the refill path to post another buffer.
|
||||
* EFX_RXD_HEAD_ROOM ensures that the slot we are using
|
||||
* isn't overwritten yet.
|
||||
*/
|
||||
rx_queue->removed_count++;
|
||||
|
||||
/* Validate the length encoded in the event vs the descriptor pushed */
|
||||
efx_rx_packet__check_len(rx_queue, rx_buf, len,
|
||||
&discard, &leak_packet);
|
||||
|
||||
EFX_TRACE(efx, "RX queue %d received id %x at %llx+%x %s%s\n",
|
||||
rx_queue->queue, index,
|
||||
(unsigned long long)rx_buf->dma_addr, len,
|
||||
(checksummed ? " [SUMMED]" : ""),
|
||||
(discard ? " [DISCARD]" : ""));
|
||||
|
||||
/* Discard packet, if instructed to do so */
|
||||
if (unlikely(discard)) {
|
||||
if (unlikely(leak_packet))
|
||||
rx_queue->channel->n_skbuff_leaks++;
|
||||
else
|
||||
/* We haven't called efx_unmap_rx_buffer yet,
|
||||
* so fini the entire rx_buffer here */
|
||||
efx_fini_rx_buffer(rx_queue, rx_buf);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Release card resources - assumes all RX buffers consumed in-order
|
||||
* per RX queue
|
||||
*/
|
||||
efx_unmap_rx_buffer(efx, rx_buf);
|
||||
|
||||
/* Prefetch nice and early so data will (hopefully) be in cache by
|
||||
* the time we look at it.
|
||||
*/
|
||||
prefetch(rx_buf->data);
|
||||
|
||||
/* Pipeline receives so that we give time for packet headers to be
|
||||
* prefetched into cache.
|
||||
*/
|
||||
rx_buf->len = len;
|
||||
if (rx_queue->channel->rx_pkt)
|
||||
__efx_rx_packet(rx_queue->channel,
|
||||
rx_queue->channel->rx_pkt,
|
||||
rx_queue->channel->rx_pkt_csummed);
|
||||
rx_queue->channel->rx_pkt = rx_buf;
|
||||
rx_queue->channel->rx_pkt_csummed = checksummed;
|
||||
}
|
||||
|
||||
/* Handle a received packet. Second half: Touches packet payload. */
|
||||
void __efx_rx_packet(struct efx_channel *channel,
|
||||
struct efx_rx_buffer *rx_buf, int checksummed)
|
||||
{
|
||||
struct efx_nic *efx = channel->efx;
|
||||
struct sk_buff *skb;
|
||||
int lro = efx->net_dev->features & NETIF_F_LRO;
|
||||
|
||||
if (rx_buf->skb) {
|
||||
prefetch(skb_shinfo(rx_buf->skb));
|
||||
|
||||
skb_put(rx_buf->skb, rx_buf->len);
|
||||
|
||||
/* Move past the ethernet header. rx_buf->data still points
|
||||
* at the ethernet header */
|
||||
rx_buf->skb->protocol = eth_type_trans(rx_buf->skb,
|
||||
efx->net_dev);
|
||||
}
|
||||
|
||||
/* Both our generic-LRO and SFC-SSR support skb and page based
|
||||
* allocation, but neither support switching from one to the
|
||||
* other on the fly. If we spot that the allocation mode has
|
||||
* changed, then flush the LRO state.
|
||||
*/
|
||||
if (unlikely(channel->rx_alloc_pop_pages != (rx_buf->page != NULL))) {
|
||||
efx_flush_lro(channel);
|
||||
channel->rx_alloc_pop_pages = (rx_buf->page != NULL);
|
||||
}
|
||||
if (likely(checksummed && lro)) {
|
||||
efx_rx_packet_lro(channel, rx_buf);
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Form an skb if required */
|
||||
if (rx_buf->page) {
|
||||
int hdr_len = min(rx_buf->len, EFX_SKB_HEADERS);
|
||||
skb = efx_rx_mk_skb(rx_buf, efx, hdr_len);
|
||||
if (unlikely(skb == NULL)) {
|
||||
efx_free_rx_buffer(efx, rx_buf);
|
||||
goto done;
|
||||
}
|
||||
} else {
|
||||
/* We now own the SKB */
|
||||
skb = rx_buf->skb;
|
||||
rx_buf->skb = NULL;
|
||||
}
|
||||
|
||||
EFX_BUG_ON_PARANOID(rx_buf->page);
|
||||
EFX_BUG_ON_PARANOID(rx_buf->skb);
|
||||
EFX_BUG_ON_PARANOID(!skb);
|
||||
|
||||
/* Set the SKB flags */
|
||||
if (unlikely(!checksummed || !efx->rx_checksum_enabled))
|
||||
skb->ip_summed = CHECKSUM_NONE;
|
||||
|
||||
/* Pass the packet up */
|
||||
netif_receive_skb(skb);
|
||||
|
||||
/* Update allocation strategy method */
|
||||
channel->rx_alloc_level += RX_ALLOC_FACTOR_SKB;
|
||||
|
||||
/* fall-thru */
|
||||
done:
|
||||
efx->net_dev->last_rx = jiffies;
|
||||
}
|
||||
|
||||
void efx_rx_strategy(struct efx_channel *channel)
|
||||
{
|
||||
enum efx_rx_alloc_method method = rx_alloc_method;
|
||||
|
||||
/* Only makes sense to use page based allocation if LRO is enabled */
|
||||
if (!(channel->efx->net_dev->features & NETIF_F_LRO)) {
|
||||
method = RX_ALLOC_METHOD_SKB;
|
||||
} else if (method == RX_ALLOC_METHOD_AUTO) {
|
||||
/* Constrain the rx_alloc_level */
|
||||
if (channel->rx_alloc_level < 0)
|
||||
channel->rx_alloc_level = 0;
|
||||
else if (channel->rx_alloc_level > RX_ALLOC_LEVEL_MAX)
|
||||
channel->rx_alloc_level = RX_ALLOC_LEVEL_MAX;
|
||||
|
||||
/* Decide on the allocation method */
|
||||
method = ((channel->rx_alloc_level > RX_ALLOC_LEVEL_LRO) ?
|
||||
RX_ALLOC_METHOD_PAGE : RX_ALLOC_METHOD_SKB);
|
||||
}
|
||||
|
||||
/* Push the option */
|
||||
channel->rx_alloc_push_pages = (method == RX_ALLOC_METHOD_PAGE);
|
||||
}
|
||||
|
||||
int efx_probe_rx_queue(struct efx_rx_queue *rx_queue)
|
||||
{
|
||||
struct efx_nic *efx = rx_queue->efx;
|
||||
unsigned int rxq_size;
|
||||
int rc;
|
||||
|
||||
EFX_LOG(efx, "creating RX queue %d\n", rx_queue->queue);
|
||||
|
||||
/* Allocate RX buffers */
|
||||
rxq_size = (efx->type->rxd_ring_mask + 1) * sizeof(*rx_queue->buffer);
|
||||
rx_queue->buffer = kzalloc(rxq_size, GFP_KERNEL);
|
||||
if (!rx_queue->buffer) {
|
||||
rc = -ENOMEM;
|
||||
goto fail1;
|
||||
}
|
||||
|
||||
rc = falcon_probe_rx(rx_queue);
|
||||
if (rc)
|
||||
goto fail2;
|
||||
|
||||
return 0;
|
||||
|
||||
fail2:
|
||||
kfree(rx_queue->buffer);
|
||||
rx_queue->buffer = NULL;
|
||||
fail1:
|
||||
rx_queue->used = 0;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int efx_init_rx_queue(struct efx_rx_queue *rx_queue)
|
||||
{
|
||||
struct efx_nic *efx = rx_queue->efx;
|
||||
unsigned int max_fill, trigger, limit;
|
||||
|
||||
EFX_LOG(rx_queue->efx, "initialising RX queue %d\n", rx_queue->queue);
|
||||
|
||||
/* Initialise ptr fields */
|
||||
rx_queue->added_count = 0;
|
||||
rx_queue->notified_count = 0;
|
||||
rx_queue->removed_count = 0;
|
||||
rx_queue->min_fill = -1U;
|
||||
rx_queue->min_overfill = -1U;
|
||||
|
||||
/* Initialise limit fields */
|
||||
max_fill = efx->type->rxd_ring_mask + 1 - EFX_RXD_HEAD_ROOM;
|
||||
trigger = max_fill * min(rx_refill_threshold, 100U) / 100U;
|
||||
limit = max_fill * min(rx_refill_limit, 100U) / 100U;
|
||||
|
||||
rx_queue->max_fill = max_fill;
|
||||
rx_queue->fast_fill_trigger = trigger;
|
||||
rx_queue->fast_fill_limit = limit;
|
||||
|
||||
/* Set up RX descriptor ring */
|
||||
return falcon_init_rx(rx_queue);
|
||||
}
|
||||
|
||||
void efx_fini_rx_queue(struct efx_rx_queue *rx_queue)
|
||||
{
|
||||
int i;
|
||||
struct efx_rx_buffer *rx_buf;
|
||||
|
||||
EFX_LOG(rx_queue->efx, "shutting down RX queue %d\n", rx_queue->queue);
|
||||
|
||||
falcon_fini_rx(rx_queue);
|
||||
|
||||
/* Release RX buffers NB start at index 0 not current HW ptr */
|
||||
if (rx_queue->buffer) {
|
||||
for (i = 0; i <= rx_queue->efx->type->rxd_ring_mask; i++) {
|
||||
rx_buf = efx_rx_buffer(rx_queue, i);
|
||||
efx_fini_rx_buffer(rx_queue, rx_buf);
|
||||
}
|
||||
}
|
||||
|
||||
/* For a page that is part-way through splitting into RX buffers */
|
||||
if (rx_queue->buf_page != NULL) {
|
||||
pci_unmap_page(rx_queue->efx->pci_dev, rx_queue->buf_dma_addr,
|
||||
RX_PAGE_SIZE(rx_queue->efx), PCI_DMA_FROMDEVICE);
|
||||
__free_pages(rx_queue->buf_page,
|
||||
rx_queue->efx->rx_buffer_order);
|
||||
rx_queue->buf_page = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void efx_remove_rx_queue(struct efx_rx_queue *rx_queue)
|
||||
{
|
||||
EFX_LOG(rx_queue->efx, "destroying RX queue %d\n", rx_queue->queue);
|
||||
|
||||
falcon_remove_rx(rx_queue);
|
||||
|
||||
kfree(rx_queue->buffer);
|
||||
rx_queue->buffer = NULL;
|
||||
rx_queue->used = 0;
|
||||
}
|
||||
|
||||
void efx_flush_lro(struct efx_channel *channel)
|
||||
{
|
||||
lro_flush_all(&channel->lro_mgr);
|
||||
}
|
||||
|
||||
|
||||
module_param(rx_alloc_method, int, 0644);
|
||||
MODULE_PARM_DESC(rx_alloc_method, "Allocation method used for RX buffers");
|
||||
|
||||
module_param(rx_refill_threshold, uint, 0444);
|
||||
MODULE_PARM_DESC(rx_refill_threshold,
|
||||
"RX descriptor ring fast/slow fill threshold (%)");
|
||||
|
29
drivers/net/sfc/rx.h
Normal file
29
drivers/net/sfc/rx.h
Normal file
@ -0,0 +1,29 @@
|
||||
/****************************************************************************
|
||||
* Driver for Solarflare Solarstorm network controllers and boards
|
||||
* Copyright 2006 Solarflare Communications Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published
|
||||
* by the Free Software Foundation, incorporated herein by reference.
|
||||
*/
|
||||
|
||||
#ifndef EFX_RX_H
|
||||
#define EFX_RX_H
|
||||
|
||||
#include "net_driver.h"
|
||||
|
||||
int efx_probe_rx_queue(struct efx_rx_queue *rx_queue);
|
||||
void efx_remove_rx_queue(struct efx_rx_queue *rx_queue);
|
||||
int efx_init_rx_queue(struct efx_rx_queue *rx_queue);
|
||||
void efx_fini_rx_queue(struct efx_rx_queue *rx_queue);
|
||||
|
||||
int efx_lro_init(struct net_lro_mgr *lro_mgr, struct efx_nic *efx);
|
||||
void efx_lro_fini(struct net_lro_mgr *lro_mgr);
|
||||
void efx_flush_lro(struct efx_channel *channel);
|
||||
void efx_rx_strategy(struct efx_channel *channel);
|
||||
void efx_fast_push_rx_descriptors(struct efx_rx_queue *rx_queue);
|
||||
void efx_rx_work(struct work_struct *data);
|
||||
void __efx_rx_packet(struct efx_channel *channel,
|
||||
struct efx_rx_buffer *rx_buf, int checksummed);
|
||||
|
||||
#endif /* EFX_RX_H */
|
252
drivers/net/sfc/sfe4001.c
Normal file
252
drivers/net/sfc/sfe4001.c
Normal file
@ -0,0 +1,252 @@
|
||||
/****************************************************************************
|
||||
* Driver for Solarflare Solarstorm network controllers and boards
|
||||
* Copyright 2007 Solarflare Communications Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published
|
||||
* by the Free Software Foundation, incorporated herein by reference.
|
||||
*/
|
||||
|
||||
/*****************************************************************************
|
||||
* Support for the SFE4001 NIC: driver code for the PCA9539 I/O expander that
|
||||
* controls the PHY power rails, and for the MAX6647 temp. sensor used to check
|
||||
* the PHY
|
||||
*/
|
||||
#include <linux/delay.h>
|
||||
#include "efx.h"
|
||||
#include "phy.h"
|
||||
#include "boards.h"
|
||||
#include "falcon.h"
|
||||
#include "falcon_hwdefs.h"
|
||||
#include "mac.h"
|
||||
|
||||
/**************************************************************************
|
||||
*
|
||||
* I2C IO Expander device
|
||||
*
|
||||
**************************************************************************/
|
||||
#define PCA9539 0x74
|
||||
|
||||
#define P0_IN 0x00
|
||||
#define P0_OUT 0x02
|
||||
#define P0_INVERT 0x04
|
||||
#define P0_CONFIG 0x06
|
||||
|
||||
#define P0_EN_1V0X_LBN 0
|
||||
#define P0_EN_1V0X_WIDTH 1
|
||||
#define P0_EN_1V2_LBN 1
|
||||
#define P0_EN_1V2_WIDTH 1
|
||||
#define P0_EN_2V5_LBN 2
|
||||
#define P0_EN_2V5_WIDTH 1
|
||||
#define P0_EN_3V3X_LBN 3
|
||||
#define P0_EN_3V3X_WIDTH 1
|
||||
#define P0_EN_5V_LBN 4
|
||||
#define P0_EN_5V_WIDTH 1
|
||||
#define P0_SHORTEN_JTAG_LBN 5
|
||||
#define P0_SHORTEN_JTAG_WIDTH 1
|
||||
#define P0_X_TRST_LBN 6
|
||||
#define P0_X_TRST_WIDTH 1
|
||||
#define P0_DSP_RESET_LBN 7
|
||||
#define P0_DSP_RESET_WIDTH 1
|
||||
|
||||
#define P1_IN 0x01
|
||||
#define P1_OUT 0x03
|
||||
#define P1_INVERT 0x05
|
||||
#define P1_CONFIG 0x07
|
||||
|
||||
#define P1_AFE_PWD_LBN 0
|
||||
#define P1_AFE_PWD_WIDTH 1
|
||||
#define P1_DSP_PWD25_LBN 1
|
||||
#define P1_DSP_PWD25_WIDTH 1
|
||||
#define P1_RESERVED_LBN 2
|
||||
#define P1_RESERVED_WIDTH 2
|
||||
#define P1_SPARE_LBN 4
|
||||
#define P1_SPARE_WIDTH 4
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
*
|
||||
* Temperature Sensor
|
||||
*
|
||||
**************************************************************************/
|
||||
#define MAX6647 0x4e
|
||||
|
||||
#define RLTS 0x00
|
||||
#define RLTE 0x01
|
||||
#define RSL 0x02
|
||||
#define RCL 0x03
|
||||
#define RCRA 0x04
|
||||
#define RLHN 0x05
|
||||
#define RLLI 0x06
|
||||
#define RRHI 0x07
|
||||
#define RRLS 0x08
|
||||
#define WCRW 0x0a
|
||||
#define WLHO 0x0b
|
||||
#define WRHA 0x0c
|
||||
#define WRLN 0x0e
|
||||
#define OSHT 0x0f
|
||||
#define REET 0x10
|
||||
#define RIET 0x11
|
||||
#define RWOE 0x19
|
||||
#define RWOI 0x20
|
||||
#define HYS 0x21
|
||||
#define QUEUE 0x22
|
||||
#define MFID 0xfe
|
||||
#define REVID 0xff
|
||||
|
||||
/* Status bits */
|
||||
#define MAX6647_BUSY (1 << 7) /* ADC is converting */
|
||||
#define MAX6647_LHIGH (1 << 6) /* Local high temp. alarm */
|
||||
#define MAX6647_LLOW (1 << 5) /* Local low temp. alarm */
|
||||
#define MAX6647_RHIGH (1 << 4) /* Remote high temp. alarm */
|
||||
#define MAX6647_RLOW (1 << 3) /* Remote low temp. alarm */
|
||||
#define MAX6647_FAULT (1 << 2) /* DXN/DXP short/open circuit */
|
||||
#define MAX6647_EOT (1 << 1) /* Remote junction overtemp. */
|
||||
#define MAX6647_IOT (1 << 0) /* Local junction overtemp. */
|
||||
|
||||
static const u8 xgphy_max_temperature = 90;
|
||||
|
||||
void sfe4001_poweroff(struct efx_nic *efx)
|
||||
{
|
||||
struct efx_i2c_interface *i2c = &efx->i2c;
|
||||
|
||||
u8 cfg, out, in;
|
||||
|
||||
EFX_INFO(efx, "%s\n", __func__);
|
||||
|
||||
/* Turn off all power rails */
|
||||
out = 0xff;
|
||||
(void) efx_i2c_write(i2c, PCA9539, P0_OUT, &out, 1);
|
||||
|
||||
/* Disable port 1 outputs on IO expander */
|
||||
cfg = 0xff;
|
||||
(void) efx_i2c_write(i2c, PCA9539, P1_CONFIG, &cfg, 1);
|
||||
|
||||
/* Disable port 0 outputs on IO expander */
|
||||
cfg = 0xff;
|
||||
(void) efx_i2c_write(i2c, PCA9539, P0_CONFIG, &cfg, 1);
|
||||
|
||||
/* Clear any over-temperature alert */
|
||||
(void) efx_i2c_read(i2c, MAX6647, RSL, &in, 1);
|
||||
}
|
||||
|
||||
/* This board uses an I2C expander to provider power to the PHY, which needs to
|
||||
* be turned on before the PHY can be used.
|
||||
* Context: Process context, rtnl lock held
|
||||
*/
|
||||
int sfe4001_poweron(struct efx_nic *efx)
|
||||
{
|
||||
struct efx_i2c_interface *i2c = &efx->i2c;
|
||||
unsigned int count;
|
||||
int rc;
|
||||
u8 out, in, cfg;
|
||||
efx_dword_t reg;
|
||||
|
||||
/* 10Xpress has fixed-function LED pins, so there is no board-specific
|
||||
* blink code. */
|
||||
efx->board_info.blink = tenxpress_phy_blink;
|
||||
|
||||
/* Ensure that XGXS and XAUI SerDes are held in reset */
|
||||
EFX_POPULATE_DWORD_7(reg, XX_PWRDNA_EN, 1,
|
||||
XX_PWRDNB_EN, 1,
|
||||
XX_RSTPLLAB_EN, 1,
|
||||
XX_RESETA_EN, 1,
|
||||
XX_RESETB_EN, 1,
|
||||
XX_RSTXGXSRX_EN, 1,
|
||||
XX_RSTXGXSTX_EN, 1);
|
||||
falcon_xmac_writel(efx, ®, XX_PWR_RST_REG_MAC);
|
||||
udelay(10);
|
||||
|
||||
/* Set DSP over-temperature alert threshold */
|
||||
EFX_INFO(efx, "DSP cut-out at %dC\n", xgphy_max_temperature);
|
||||
rc = efx_i2c_write(i2c, MAX6647, WLHO,
|
||||
&xgphy_max_temperature, 1);
|
||||
if (rc)
|
||||
goto fail1;
|
||||
|
||||
/* Read it back and verify */
|
||||
rc = efx_i2c_read(i2c, MAX6647, RLHN, &in, 1);
|
||||
if (rc)
|
||||
goto fail1;
|
||||
if (in != xgphy_max_temperature) {
|
||||
rc = -EFAULT;
|
||||
goto fail1;
|
||||
}
|
||||
|
||||
/* Clear any previous over-temperature alert */
|
||||
rc = efx_i2c_read(i2c, MAX6647, RSL, &in, 1);
|
||||
if (rc)
|
||||
goto fail1;
|
||||
|
||||
/* Enable port 0 and port 1 outputs on IO expander */
|
||||
cfg = 0x00;
|
||||
rc = efx_i2c_write(i2c, PCA9539, P0_CONFIG, &cfg, 1);
|
||||
if (rc)
|
||||
goto fail1;
|
||||
cfg = 0xff & ~(1 << P1_SPARE_LBN);
|
||||
rc = efx_i2c_write(i2c, PCA9539, P1_CONFIG, &cfg, 1);
|
||||
if (rc)
|
||||
goto fail2;
|
||||
|
||||
/* Turn all power off then wait 1 sec. This ensures PHY is reset */
|
||||
out = 0xff & ~((0 << P0_EN_1V2_LBN) | (0 << P0_EN_2V5_LBN) |
|
||||
(0 << P0_EN_3V3X_LBN) | (0 << P0_EN_5V_LBN) |
|
||||
(0 << P0_EN_1V0X_LBN));
|
||||
rc = efx_i2c_write(i2c, PCA9539, P0_OUT, &out, 1);
|
||||
if (rc)
|
||||
goto fail3;
|
||||
|
||||
schedule_timeout_uninterruptible(HZ);
|
||||
count = 0;
|
||||
do {
|
||||
/* Turn on 1.2V, 2.5V, 3.3V and 5V power rails */
|
||||
out = 0xff & ~((1 << P0_EN_1V2_LBN) | (1 << P0_EN_2V5_LBN) |
|
||||
(1 << P0_EN_3V3X_LBN) | (1 << P0_EN_5V_LBN) |
|
||||
(1 << P0_X_TRST_LBN));
|
||||
|
||||
rc = efx_i2c_write(i2c, PCA9539, P0_OUT, &out, 1);
|
||||
if (rc)
|
||||
goto fail3;
|
||||
msleep(10);
|
||||
|
||||
/* Turn on 1V power rail */
|
||||
out &= ~(1 << P0_EN_1V0X_LBN);
|
||||
rc = efx_i2c_write(i2c, PCA9539, P0_OUT, &out, 1);
|
||||
if (rc)
|
||||
goto fail3;
|
||||
|
||||
EFX_INFO(efx, "waiting for power (attempt %d)...\n", count);
|
||||
|
||||
schedule_timeout_uninterruptible(HZ);
|
||||
|
||||
/* Check DSP is powered */
|
||||
rc = efx_i2c_read(i2c, PCA9539, P1_IN, &in, 1);
|
||||
if (rc)
|
||||
goto fail3;
|
||||
if (in & (1 << P1_AFE_PWD_LBN))
|
||||
goto done;
|
||||
|
||||
} while (++count < 20);
|
||||
|
||||
EFX_INFO(efx, "timed out waiting for power\n");
|
||||
rc = -ETIMEDOUT;
|
||||
goto fail3;
|
||||
|
||||
done:
|
||||
EFX_INFO(efx, "PHY is powered on\n");
|
||||
return 0;
|
||||
|
||||
fail3:
|
||||
/* Turn off all power rails */
|
||||
out = 0xff;
|
||||
(void) efx_i2c_write(i2c, PCA9539, P0_OUT, &out, 1);
|
||||
/* Disable port 1 outputs on IO expander */
|
||||
out = 0xff;
|
||||
(void) efx_i2c_write(i2c, PCA9539, P1_CONFIG, &out, 1);
|
||||
fail2:
|
||||
/* Disable port 0 outputs on IO expander */
|
||||
out = 0xff;
|
||||
(void) efx_i2c_write(i2c, PCA9539, P0_CONFIG, &out, 1);
|
||||
fail1:
|
||||
return rc;
|
||||
}
|
71
drivers/net/sfc/spi.h
Normal file
71
drivers/net/sfc/spi.h
Normal file
@ -0,0 +1,71 @@
|
||||
/****************************************************************************
|
||||
* Driver for Solarflare Solarstorm network controllers and boards
|
||||
* Copyright 2005 Fen Systems Ltd.
|
||||
* Copyright 2006 Solarflare Communications Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published
|
||||
* by the Free Software Foundation, incorporated herein by reference.
|
||||
*/
|
||||
|
||||
#ifndef EFX_SPI_H
|
||||
#define EFX_SPI_H
|
||||
|
||||
#include "net_driver.h"
|
||||
|
||||
/**************************************************************************
|
||||
*
|
||||
* Basic SPI command set and bit definitions
|
||||
*
|
||||
*************************************************************************/
|
||||
|
||||
/*
|
||||
* Commands common to all known devices.
|
||||
*
|
||||
*/
|
||||
|
||||
/* Write status register */
|
||||
#define SPI_WRSR 0x01
|
||||
|
||||
/* Write data to memory array */
|
||||
#define SPI_WRITE 0x02
|
||||
|
||||
/* Read data from memory array */
|
||||
#define SPI_READ 0x03
|
||||
|
||||
/* Reset write enable latch */
|
||||
#define SPI_WRDI 0x04
|
||||
|
||||
/* Read status register */
|
||||
#define SPI_RDSR 0x05
|
||||
|
||||
/* Set write enable latch */
|
||||
#define SPI_WREN 0x06
|
||||
|
||||
/* SST: Enable write to status register */
|
||||
#define SPI_SST_EWSR 0x50
|
||||
|
||||
/*
|
||||
* Status register bits. Not all bits are supported on all devices.
|
||||
*
|
||||
*/
|
||||
|
||||
/* Write-protect pin enabled */
|
||||
#define SPI_STATUS_WPEN 0x80
|
||||
|
||||
/* Block protection bit 2 */
|
||||
#define SPI_STATUS_BP2 0x10
|
||||
|
||||
/* Block protection bit 1 */
|
||||
#define SPI_STATUS_BP1 0x08
|
||||
|
||||
/* Block protection bit 0 */
|
||||
#define SPI_STATUS_BP0 0x04
|
||||
|
||||
/* State of the write enable latch */
|
||||
#define SPI_STATUS_WEN 0x02
|
||||
|
||||
/* Device busy flag */
|
||||
#define SPI_STATUS_NRDY 0x01
|
||||
|
||||
#endif /* EFX_SPI_H */
|
434
drivers/net/sfc/tenxpress.c
Normal file
434
drivers/net/sfc/tenxpress.c
Normal file
@ -0,0 +1,434 @@
|
||||
/****************************************************************************
|
||||
* Driver for Solarflare 802.3an compliant PHY
|
||||
* Copyright 2007 Solarflare Communications Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published
|
||||
* by the Free Software Foundation, incorporated herein by reference.
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include "efx.h"
|
||||
#include "gmii.h"
|
||||
#include "mdio_10g.h"
|
||||
#include "falcon.h"
|
||||
#include "phy.h"
|
||||
#include "falcon_hwdefs.h"
|
||||
#include "boards.h"
|
||||
#include "mac.h"
|
||||
|
||||
/* We expect these MMDs to be in the package */
|
||||
/* AN not here as mdio_check_mmds() requires STAT2 support */
|
||||
#define TENXPRESS_REQUIRED_DEVS (MDIO_MMDREG_DEVS0_PMAPMD | \
|
||||
MDIO_MMDREG_DEVS0_PCS | \
|
||||
MDIO_MMDREG_DEVS0_PHYXS)
|
||||
|
||||
/* We complain if we fail to see the link partner as 10G capable this many
|
||||
* times in a row (must be > 1 as sampling the autoneg. registers is racy)
|
||||
*/
|
||||
#define MAX_BAD_LP_TRIES (5)
|
||||
|
||||
/* Extended control register */
|
||||
#define PMA_PMD_XCONTROL_REG 0xc000
|
||||
#define PMA_PMD_LNPGA_POWERDOWN_LBN 8
|
||||
#define PMA_PMD_LNPGA_POWERDOWN_WIDTH 1
|
||||
|
||||
/* extended status register */
|
||||
#define PMA_PMD_XSTATUS_REG 0xc001
|
||||
#define PMA_PMD_XSTAT_FLP_LBN (12)
|
||||
|
||||
/* LED control register */
|
||||
#define PMA_PMD_LED_CTRL_REG (0xc007)
|
||||
#define PMA_PMA_LED_ACTIVITY_LBN (3)
|
||||
|
||||
/* LED function override register */
|
||||
#define PMA_PMD_LED_OVERR_REG (0xc009)
|
||||
/* Bit positions for different LEDs (there are more but not wired on SFE4001)*/
|
||||
#define PMA_PMD_LED_LINK_LBN (0)
|
||||
#define PMA_PMD_LED_SPEED_LBN (2)
|
||||
#define PMA_PMD_LED_TX_LBN (4)
|
||||
#define PMA_PMD_LED_RX_LBN (6)
|
||||
/* Override settings */
|
||||
#define PMA_PMD_LED_AUTO (0) /* H/W control */
|
||||
#define PMA_PMD_LED_ON (1)
|
||||
#define PMA_PMD_LED_OFF (2)
|
||||
#define PMA_PMD_LED_FLASH (3)
|
||||
/* All LEDs under hardware control */
|
||||
#define PMA_PMD_LED_FULL_AUTO (0)
|
||||
/* Green and Amber under hardware control, Red off */
|
||||
#define PMA_PMD_LED_DEFAULT (PMA_PMD_LED_OFF << PMA_PMD_LED_RX_LBN)
|
||||
|
||||
|
||||
/* Self test (BIST) control register */
|
||||
#define PMA_PMD_BIST_CTRL_REG (0xc014)
|
||||
#define PMA_PMD_BIST_BER_LBN (2) /* Run BER test */
|
||||
#define PMA_PMD_BIST_CONT_LBN (1) /* Run continuous BIST until cleared */
|
||||
#define PMA_PMD_BIST_SINGLE_LBN (0) /* Run 1 BIST iteration (self clears) */
|
||||
/* Self test status register */
|
||||
#define PMA_PMD_BIST_STAT_REG (0xc015)
|
||||
#define PMA_PMD_BIST_ENX_LBN (3)
|
||||
#define PMA_PMD_BIST_PMA_LBN (2)
|
||||
#define PMA_PMD_BIST_RXD_LBN (1)
|
||||
#define PMA_PMD_BIST_AFE_LBN (0)
|
||||
|
||||
#define BIST_MAX_DELAY (1000)
|
||||
#define BIST_POLL_DELAY (10)
|
||||
|
||||
/* Misc register defines */
|
||||
#define PCS_CLOCK_CTRL_REG 0xd801
|
||||
#define PLL312_RST_N_LBN 2
|
||||
|
||||
#define PCS_SOFT_RST2_REG 0xd806
|
||||
#define SERDES_RST_N_LBN 13
|
||||
#define XGXS_RST_N_LBN 12
|
||||
|
||||
#define PCS_TEST_SELECT_REG 0xd807 /* PRM 10.5.8 */
|
||||
#define CLK312_EN_LBN 3
|
||||
|
||||
/* Boot status register */
|
||||
#define PCS_BOOT_STATUS_REG (0xd000)
|
||||
#define PCS_BOOT_FATAL_ERR_LBN (0)
|
||||
#define PCS_BOOT_PROGRESS_LBN (1)
|
||||
#define PCS_BOOT_PROGRESS_WIDTH (2)
|
||||
#define PCS_BOOT_COMPLETE_LBN (3)
|
||||
#define PCS_BOOT_MAX_DELAY (100)
|
||||
#define PCS_BOOT_POLL_DELAY (10)
|
||||
|
||||
/* Time to wait between powering down the LNPGA and turning off the power
|
||||
* rails */
|
||||
#define LNPGA_PDOWN_WAIT (HZ / 5)
|
||||
|
||||
static int crc_error_reset_threshold = 100;
|
||||
module_param(crc_error_reset_threshold, int, 0644);
|
||||
MODULE_PARM_DESC(crc_error_reset_threshold,
|
||||
"Max number of CRC errors before XAUI reset");
|
||||
|
||||
struct tenxpress_phy_data {
|
||||
enum tenxpress_state state;
|
||||
atomic_t bad_crc_count;
|
||||
int bad_lp_tries;
|
||||
};
|
||||
|
||||
static int tenxpress_state_is(struct efx_nic *efx, int state)
|
||||
{
|
||||
struct tenxpress_phy_data *phy_data = efx->phy_data;
|
||||
return (phy_data != NULL) && (state == phy_data->state);
|
||||
}
|
||||
|
||||
void tenxpress_set_state(struct efx_nic *efx,
|
||||
enum tenxpress_state state)
|
||||
{
|
||||
struct tenxpress_phy_data *phy_data = efx->phy_data;
|
||||
if (phy_data != NULL)
|
||||
phy_data->state = state;
|
||||
}
|
||||
|
||||
void tenxpress_crc_err(struct efx_nic *efx)
|
||||
{
|
||||
struct tenxpress_phy_data *phy_data = efx->phy_data;
|
||||
if (phy_data != NULL)
|
||||
atomic_inc(&phy_data->bad_crc_count);
|
||||
}
|
||||
|
||||
/* Check that the C166 has booted successfully */
|
||||
static int tenxpress_phy_check(struct efx_nic *efx)
|
||||
{
|
||||
int phy_id = efx->mii.phy_id;
|
||||
int count = PCS_BOOT_MAX_DELAY / PCS_BOOT_POLL_DELAY;
|
||||
int boot_stat;
|
||||
|
||||
/* Wait for the boot to complete (or not) */
|
||||
while (count) {
|
||||
boot_stat = mdio_clause45_read(efx, phy_id,
|
||||
MDIO_MMD_PCS,
|
||||
PCS_BOOT_STATUS_REG);
|
||||
if (boot_stat & (1 << PCS_BOOT_COMPLETE_LBN))
|
||||
break;
|
||||
count--;
|
||||
udelay(PCS_BOOT_POLL_DELAY);
|
||||
}
|
||||
|
||||
if (!count) {
|
||||
EFX_ERR(efx, "%s: PHY boot timed out. Last status "
|
||||
"%x\n", __func__,
|
||||
(boot_stat >> PCS_BOOT_PROGRESS_LBN) &
|
||||
((1 << PCS_BOOT_PROGRESS_WIDTH) - 1));
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void tenxpress_reset_xaui(struct efx_nic *efx);
|
||||
|
||||
static int tenxpress_init(struct efx_nic *efx)
|
||||
{
|
||||
int rc, reg;
|
||||
|
||||
/* Turn on the clock */
|
||||
reg = (1 << CLK312_EN_LBN);
|
||||
mdio_clause45_write(efx, efx->mii.phy_id,
|
||||
MDIO_MMD_PCS, PCS_TEST_SELECT_REG, reg);
|
||||
|
||||
rc = tenxpress_phy_check(efx);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
/* Set the LEDs up as: Green = Link, Amber = Link/Act, Red = Off */
|
||||
reg = mdio_clause45_read(efx, efx->mii.phy_id,
|
||||
MDIO_MMD_PMAPMD, PMA_PMD_LED_CTRL_REG);
|
||||
reg |= (1 << PMA_PMA_LED_ACTIVITY_LBN);
|
||||
mdio_clause45_write(efx, efx->mii.phy_id, MDIO_MMD_PMAPMD,
|
||||
PMA_PMD_LED_CTRL_REG, reg);
|
||||
|
||||
reg = PMA_PMD_LED_DEFAULT;
|
||||
mdio_clause45_write(efx, efx->mii.phy_id, MDIO_MMD_PMAPMD,
|
||||
PMA_PMD_LED_OVERR_REG, reg);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int tenxpress_phy_init(struct efx_nic *efx)
|
||||
{
|
||||
struct tenxpress_phy_data *phy_data;
|
||||
int rc = 0;
|
||||
|
||||
phy_data = kzalloc(sizeof(*phy_data), GFP_KERNEL);
|
||||
efx->phy_data = phy_data;
|
||||
|
||||
tenxpress_set_state(efx, TENXPRESS_STATUS_NORMAL);
|
||||
|
||||
rc = mdio_clause45_wait_reset_mmds(efx,
|
||||
TENXPRESS_REQUIRED_DEVS);
|
||||
if (rc < 0)
|
||||
goto fail;
|
||||
|
||||
rc = mdio_clause45_check_mmds(efx, TENXPRESS_REQUIRED_DEVS, 0);
|
||||
if (rc < 0)
|
||||
goto fail;
|
||||
|
||||
rc = tenxpress_init(efx);
|
||||
if (rc < 0)
|
||||
goto fail;
|
||||
|
||||
schedule_timeout_uninterruptible(HZ / 5); /* 200ms */
|
||||
|
||||
/* Let XGXS and SerDes out of reset and resets 10XPress */
|
||||
falcon_reset_xaui(efx);
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
kfree(efx->phy_data);
|
||||
efx->phy_data = NULL;
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void tenxpress_set_bad_lp(struct efx_nic *efx, int bad_lp)
|
||||
{
|
||||
struct tenxpress_phy_data *pd = efx->phy_data;
|
||||
int reg;
|
||||
|
||||
/* Nothing to do if all is well and was previously so. */
|
||||
if (!(bad_lp || pd->bad_lp_tries))
|
||||
return;
|
||||
|
||||
reg = mdio_clause45_read(efx, efx->mii.phy_id,
|
||||
MDIO_MMD_PMAPMD, PMA_PMD_LED_OVERR_REG);
|
||||
|
||||
if (bad_lp)
|
||||
pd->bad_lp_tries++;
|
||||
else
|
||||
pd->bad_lp_tries = 0;
|
||||
|
||||
if (pd->bad_lp_tries == MAX_BAD_LP_TRIES) {
|
||||
pd->bad_lp_tries = 0; /* Restart count */
|
||||
reg &= ~(PMA_PMD_LED_FLASH << PMA_PMD_LED_RX_LBN);
|
||||
reg |= (PMA_PMD_LED_FLASH << PMA_PMD_LED_RX_LBN);
|
||||
EFX_ERR(efx, "This NIC appears to be plugged into"
|
||||
" a port that is not 10GBASE-T capable.\n"
|
||||
" This PHY is 10GBASE-T ONLY, so no link can"
|
||||
" be established.\n");
|
||||
} else {
|
||||
reg |= (PMA_PMD_LED_OFF << PMA_PMD_LED_RX_LBN);
|
||||
}
|
||||
mdio_clause45_write(efx, efx->mii.phy_id, MDIO_MMD_PMAPMD,
|
||||
PMA_PMD_LED_OVERR_REG, reg);
|
||||
}
|
||||
|
||||
/* Check link status and return a boolean OK value. If the link is NOT
|
||||
* OK we have a quick rummage round to see if we appear to be plugged
|
||||
* into a non-10GBT port and if so warn the user that they won't get
|
||||
* link any time soon as we are 10GBT only, unless caller specified
|
||||
* not to do this check (it isn't useful in loopback) */
|
||||
static int tenxpress_link_ok(struct efx_nic *efx, int check_lp)
|
||||
{
|
||||
int ok = mdio_clause45_links_ok(efx, TENXPRESS_REQUIRED_DEVS);
|
||||
|
||||
if (ok) {
|
||||
tenxpress_set_bad_lp(efx, 0);
|
||||
} else if (check_lp) {
|
||||
/* Are we plugged into the wrong sort of link? */
|
||||
int bad_lp = 0;
|
||||
int phy_id = efx->mii.phy_id;
|
||||
int an_stat = mdio_clause45_read(efx, phy_id, MDIO_MMD_AN,
|
||||
MDIO_AN_STATUS);
|
||||
int xphy_stat = mdio_clause45_read(efx, phy_id,
|
||||
MDIO_MMD_PMAPMD,
|
||||
PMA_PMD_XSTATUS_REG);
|
||||
/* Are we plugged into anything that sends FLPs? If
|
||||
* not we can't distinguish between not being plugged
|
||||
* in and being plugged into a non-AN antique. The FLP
|
||||
* bit has the advantage of not clearing when autoneg
|
||||
* restarts. */
|
||||
if (!(xphy_stat & (1 << PMA_PMD_XSTAT_FLP_LBN))) {
|
||||
tenxpress_set_bad_lp(efx, 0);
|
||||
return ok;
|
||||
}
|
||||
|
||||
/* If it can do 10GBT it must be XNP capable */
|
||||
bad_lp = !(an_stat & (1 << MDIO_AN_STATUS_XNP_LBN));
|
||||
if (!bad_lp && (an_stat & (1 << MDIO_AN_STATUS_PAGE_LBN))) {
|
||||
bad_lp = !(mdio_clause45_read(efx, phy_id,
|
||||
MDIO_MMD_AN, MDIO_AN_10GBT_STATUS) &
|
||||
(1 << MDIO_AN_10GBT_STATUS_LP_10G_LBN));
|
||||
}
|
||||
tenxpress_set_bad_lp(efx, bad_lp);
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
static void tenxpress_phy_reconfigure(struct efx_nic *efx)
|
||||
{
|
||||
if (!tenxpress_state_is(efx, TENXPRESS_STATUS_NORMAL))
|
||||
return;
|
||||
|
||||
efx->link_up = tenxpress_link_ok(efx, 0);
|
||||
efx->link_options = GM_LPA_10000FULL;
|
||||
}
|
||||
|
||||
static void tenxpress_phy_clear_interrupt(struct efx_nic *efx)
|
||||
{
|
||||
/* Nothing done here - LASI interrupts aren't reliable so poll */
|
||||
}
|
||||
|
||||
|
||||
/* Poll PHY for interrupt */
|
||||
static int tenxpress_phy_check_hw(struct efx_nic *efx)
|
||||
{
|
||||
struct tenxpress_phy_data *phy_data = efx->phy_data;
|
||||
int phy_up = tenxpress_state_is(efx, TENXPRESS_STATUS_NORMAL);
|
||||
int link_ok;
|
||||
|
||||
link_ok = phy_up && tenxpress_link_ok(efx, 1);
|
||||
|
||||
if (link_ok != efx->link_up)
|
||||
falcon_xmac_sim_phy_event(efx);
|
||||
|
||||
/* Nothing to check if we've already shut down the PHY */
|
||||
if (!phy_up)
|
||||
return 0;
|
||||
|
||||
if (atomic_read(&phy_data->bad_crc_count) > crc_error_reset_threshold) {
|
||||
EFX_ERR(efx, "Resetting XAUI due to too many CRC errors\n");
|
||||
falcon_reset_xaui(efx);
|
||||
atomic_set(&phy_data->bad_crc_count, 0);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void tenxpress_phy_fini(struct efx_nic *efx)
|
||||
{
|
||||
int reg;
|
||||
|
||||
/* Power down the LNPGA */
|
||||
reg = (1 << PMA_PMD_LNPGA_POWERDOWN_LBN);
|
||||
mdio_clause45_write(efx, efx->mii.phy_id, MDIO_MMD_PMAPMD,
|
||||
PMA_PMD_XCONTROL_REG, reg);
|
||||
|
||||
/* Waiting here ensures that the board fini, which can turn off the
|
||||
* power to the PHY, won't get run until the LNPGA powerdown has been
|
||||
* given long enough to complete. */
|
||||
schedule_timeout_uninterruptible(LNPGA_PDOWN_WAIT); /* 200 ms */
|
||||
|
||||
kfree(efx->phy_data);
|
||||
efx->phy_data = NULL;
|
||||
}
|
||||
|
||||
|
||||
/* Set the RX and TX LEDs and Link LED flashing. The other LEDs
|
||||
* (which probably aren't wired anyway) are left in AUTO mode */
|
||||
void tenxpress_phy_blink(struct efx_nic *efx, int blink)
|
||||
{
|
||||
int reg;
|
||||
|
||||
if (blink)
|
||||
reg = (PMA_PMD_LED_FLASH << PMA_PMD_LED_TX_LBN) |
|
||||
(PMA_PMD_LED_FLASH << PMA_PMD_LED_RX_LBN) |
|
||||
(PMA_PMD_LED_FLASH << PMA_PMD_LED_LINK_LBN);
|
||||
else
|
||||
reg = PMA_PMD_LED_DEFAULT;
|
||||
|
||||
mdio_clause45_write(efx, efx->mii.phy_id, MDIO_MMD_PMAPMD,
|
||||
PMA_PMD_LED_OVERR_REG, reg);
|
||||
}
|
||||
|
||||
static void tenxpress_reset_xaui(struct efx_nic *efx)
|
||||
{
|
||||
int phy = efx->mii.phy_id;
|
||||
int clk_ctrl, test_select, soft_rst2;
|
||||
|
||||
/* Real work is done on clock_ctrl other resets are thought to be
|
||||
* optional but make the reset more reliable
|
||||
*/
|
||||
|
||||
/* Read */
|
||||
clk_ctrl = mdio_clause45_read(efx, phy, MDIO_MMD_PCS,
|
||||
PCS_CLOCK_CTRL_REG);
|
||||
test_select = mdio_clause45_read(efx, phy, MDIO_MMD_PCS,
|
||||
PCS_TEST_SELECT_REG);
|
||||
soft_rst2 = mdio_clause45_read(efx, phy, MDIO_MMD_PCS,
|
||||
PCS_SOFT_RST2_REG);
|
||||
|
||||
/* Put in reset */
|
||||
test_select &= ~(1 << CLK312_EN_LBN);
|
||||
mdio_clause45_write(efx, phy, MDIO_MMD_PCS,
|
||||
PCS_TEST_SELECT_REG, test_select);
|
||||
|
||||
soft_rst2 &= ~((1 << XGXS_RST_N_LBN) | (1 << SERDES_RST_N_LBN));
|
||||
mdio_clause45_write(efx, phy, MDIO_MMD_PCS,
|
||||
PCS_SOFT_RST2_REG, soft_rst2);
|
||||
|
||||
clk_ctrl &= ~(1 << PLL312_RST_N_LBN);
|
||||
mdio_clause45_write(efx, phy, MDIO_MMD_PCS,
|
||||
PCS_CLOCK_CTRL_REG, clk_ctrl);
|
||||
udelay(10);
|
||||
|
||||
/* Remove reset */
|
||||
clk_ctrl |= (1 << PLL312_RST_N_LBN);
|
||||
mdio_clause45_write(efx, phy, MDIO_MMD_PCS,
|
||||
PCS_CLOCK_CTRL_REG, clk_ctrl);
|
||||
udelay(10);
|
||||
|
||||
soft_rst2 |= ((1 << XGXS_RST_N_LBN) | (1 << SERDES_RST_N_LBN));
|
||||
mdio_clause45_write(efx, phy, MDIO_MMD_PCS,
|
||||
PCS_SOFT_RST2_REG, soft_rst2);
|
||||
udelay(10);
|
||||
|
||||
test_select |= (1 << CLK312_EN_LBN);
|
||||
mdio_clause45_write(efx, phy, MDIO_MMD_PCS,
|
||||
PCS_TEST_SELECT_REG, test_select);
|
||||
udelay(10);
|
||||
}
|
||||
|
||||
struct efx_phy_operations falcon_tenxpress_phy_ops = {
|
||||
.init = tenxpress_phy_init,
|
||||
.reconfigure = tenxpress_phy_reconfigure,
|
||||
.check_hw = tenxpress_phy_check_hw,
|
||||
.fini = tenxpress_phy_fini,
|
||||
.clear_interrupt = tenxpress_phy_clear_interrupt,
|
||||
.reset_xaui = tenxpress_reset_xaui,
|
||||
.mmds = TENXPRESS_REQUIRED_DEVS,
|
||||
};
|
452
drivers/net/sfc/tx.c
Normal file
452
drivers/net/sfc/tx.c
Normal file
@ -0,0 +1,452 @@
|
||||
/****************************************************************************
|
||||
* Driver for Solarflare Solarstorm network controllers and boards
|
||||
* Copyright 2005-2006 Fen Systems Ltd.
|
||||
* Copyright 2005-2008 Solarflare Communications Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published
|
||||
* by the Free Software Foundation, incorporated herein by reference.
|
||||
*/
|
||||
|
||||
#include <linux/pci.h>
|
||||
#include <linux/tcp.h>
|
||||
#include <linux/ip.h>
|
||||
#include <linux/in.h>
|
||||
#include <linux/if_ether.h>
|
||||
#include <linux/highmem.h>
|
||||
#include "net_driver.h"
|
||||
#include "tx.h"
|
||||
#include "efx.h"
|
||||
#include "falcon.h"
|
||||
#include "workarounds.h"
|
||||
|
||||
/*
|
||||
* TX descriptor ring full threshold
|
||||
*
|
||||
* The tx_queue descriptor ring fill-level must fall below this value
|
||||
* before we restart the netif queue
|
||||
*/
|
||||
#define EFX_NETDEV_TX_THRESHOLD(_tx_queue) \
|
||||
(_tx_queue->efx->type->txd_ring_mask / 2u)
|
||||
|
||||
/* We want to be able to nest calls to netif_stop_queue(), since each
|
||||
* channel can have an individual stop on the queue.
|
||||
*/
|
||||
void efx_stop_queue(struct efx_nic *efx)
|
||||
{
|
||||
spin_lock_bh(&efx->netif_stop_lock);
|
||||
EFX_TRACE(efx, "stop TX queue\n");
|
||||
|
||||
atomic_inc(&efx->netif_stop_count);
|
||||
netif_stop_queue(efx->net_dev);
|
||||
|
||||
spin_unlock_bh(&efx->netif_stop_lock);
|
||||
}
|
||||
|
||||
/* Wake netif's TX queue
|
||||
* We want to be able to nest calls to netif_stop_queue(), since each
|
||||
* channel can have an individual stop on the queue.
|
||||
*/
|
||||
inline void efx_wake_queue(struct efx_nic *efx)
|
||||
{
|
||||
local_bh_disable();
|
||||
if (atomic_dec_and_lock(&efx->netif_stop_count,
|
||||
&efx->netif_stop_lock)) {
|
||||
EFX_TRACE(efx, "waking TX queue\n");
|
||||
netif_wake_queue(efx->net_dev);
|
||||
spin_unlock(&efx->netif_stop_lock);
|
||||
}
|
||||
local_bh_enable();
|
||||
}
|
||||
|
||||
static inline void efx_dequeue_buffer(struct efx_tx_queue *tx_queue,
|
||||
struct efx_tx_buffer *buffer)
|
||||
{
|
||||
if (buffer->unmap_len) {
|
||||
struct pci_dev *pci_dev = tx_queue->efx->pci_dev;
|
||||
if (buffer->unmap_single)
|
||||
pci_unmap_single(pci_dev, buffer->unmap_addr,
|
||||
buffer->unmap_len, PCI_DMA_TODEVICE);
|
||||
else
|
||||
pci_unmap_page(pci_dev, buffer->unmap_addr,
|
||||
buffer->unmap_len, PCI_DMA_TODEVICE);
|
||||
buffer->unmap_len = 0;
|
||||
buffer->unmap_single = 0;
|
||||
}
|
||||
|
||||
if (buffer->skb) {
|
||||
dev_kfree_skb_any((struct sk_buff *) buffer->skb);
|
||||
buffer->skb = NULL;
|
||||
EFX_TRACE(tx_queue->efx, "TX queue %d transmission id %x "
|
||||
"complete\n", tx_queue->queue, read_ptr);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Add a socket buffer to a TX queue
|
||||
*
|
||||
* This maps all fragments of a socket buffer for DMA and adds them to
|
||||
* the TX queue. The queue's insert pointer will be incremented by
|
||||
* the number of fragments in the socket buffer.
|
||||
*
|
||||
* If any DMA mapping fails, any mapped fragments will be unmapped,
|
||||
* the queue's insert pointer will be restored to its original value.
|
||||
*
|
||||
* Returns NETDEV_TX_OK or NETDEV_TX_BUSY
|
||||
* You must hold netif_tx_lock() to call this function.
|
||||
*/
|
||||
static inline int efx_enqueue_skb(struct efx_tx_queue *tx_queue,
|
||||
const struct sk_buff *skb)
|
||||
{
|
||||
struct efx_nic *efx = tx_queue->efx;
|
||||
struct pci_dev *pci_dev = efx->pci_dev;
|
||||
struct efx_tx_buffer *buffer;
|
||||
skb_frag_t *fragment;
|
||||
struct page *page;
|
||||
int page_offset;
|
||||
unsigned int len, unmap_len = 0, fill_level, insert_ptr, misalign;
|
||||
dma_addr_t dma_addr, unmap_addr = 0;
|
||||
unsigned int dma_len;
|
||||
unsigned unmap_single;
|
||||
int q_space, i = 0;
|
||||
int rc = NETDEV_TX_OK;
|
||||
|
||||
EFX_BUG_ON_PARANOID(tx_queue->write_count != tx_queue->insert_count);
|
||||
|
||||
/* Get size of the initial fragment */
|
||||
len = skb_headlen(skb);
|
||||
|
||||
fill_level = tx_queue->insert_count - tx_queue->old_read_count;
|
||||
q_space = efx->type->txd_ring_mask - 1 - fill_level;
|
||||
|
||||
/* Map for DMA. Use pci_map_single rather than pci_map_page
|
||||
* since this is more efficient on machines with sparse
|
||||
* memory.
|
||||
*/
|
||||
unmap_single = 1;
|
||||
dma_addr = pci_map_single(pci_dev, skb->data, len, PCI_DMA_TODEVICE);
|
||||
|
||||
/* Process all fragments */
|
||||
while (1) {
|
||||
if (unlikely(pci_dma_mapping_error(dma_addr)))
|
||||
goto pci_err;
|
||||
|
||||
/* Store fields for marking in the per-fragment final
|
||||
* descriptor */
|
||||
unmap_len = len;
|
||||
unmap_addr = dma_addr;
|
||||
|
||||
/* Add to TX queue, splitting across DMA boundaries */
|
||||
do {
|
||||
if (unlikely(q_space-- <= 0)) {
|
||||
/* It might be that completions have
|
||||
* happened since the xmit path last
|
||||
* checked. Update the xmit path's
|
||||
* copy of read_count.
|
||||
*/
|
||||
++tx_queue->stopped;
|
||||
/* This memory barrier protects the
|
||||
* change of stopped from the access
|
||||
* of read_count. */
|
||||
smp_mb();
|
||||
tx_queue->old_read_count =
|
||||
*(volatile unsigned *)
|
||||
&tx_queue->read_count;
|
||||
fill_level = (tx_queue->insert_count
|
||||
- tx_queue->old_read_count);
|
||||
q_space = (efx->type->txd_ring_mask - 1 -
|
||||
fill_level);
|
||||
if (unlikely(q_space-- <= 0))
|
||||
goto stop;
|
||||
smp_mb();
|
||||
--tx_queue->stopped;
|
||||
}
|
||||
|
||||
insert_ptr = (tx_queue->insert_count &
|
||||
efx->type->txd_ring_mask);
|
||||
buffer = &tx_queue->buffer[insert_ptr];
|
||||
EFX_BUG_ON_PARANOID(buffer->skb);
|
||||
EFX_BUG_ON_PARANOID(buffer->len);
|
||||
EFX_BUG_ON_PARANOID(buffer->continuation != 1);
|
||||
EFX_BUG_ON_PARANOID(buffer->unmap_len);
|
||||
|
||||
dma_len = (((~dma_addr) & efx->type->tx_dma_mask) + 1);
|
||||
if (likely(dma_len > len))
|
||||
dma_len = len;
|
||||
|
||||
misalign = (unsigned)dma_addr & efx->type->bug5391_mask;
|
||||
if (misalign && dma_len + misalign > 512)
|
||||
dma_len = 512 - misalign;
|
||||
|
||||
/* Fill out per descriptor fields */
|
||||
buffer->len = dma_len;
|
||||
buffer->dma_addr = dma_addr;
|
||||
len -= dma_len;
|
||||
dma_addr += dma_len;
|
||||
++tx_queue->insert_count;
|
||||
} while (len);
|
||||
|
||||
/* Transfer ownership of the unmapping to the final buffer */
|
||||
buffer->unmap_addr = unmap_addr;
|
||||
buffer->unmap_single = unmap_single;
|
||||
buffer->unmap_len = unmap_len;
|
||||
unmap_len = 0;
|
||||
|
||||
/* Get address and size of next fragment */
|
||||
if (i >= skb_shinfo(skb)->nr_frags)
|
||||
break;
|
||||
fragment = &skb_shinfo(skb)->frags[i];
|
||||
len = fragment->size;
|
||||
page = fragment->page;
|
||||
page_offset = fragment->page_offset;
|
||||
i++;
|
||||
/* Map for DMA */
|
||||
unmap_single = 0;
|
||||
dma_addr = pci_map_page(pci_dev, page, page_offset, len,
|
||||
PCI_DMA_TODEVICE);
|
||||
}
|
||||
|
||||
/* Transfer ownership of the skb to the final buffer */
|
||||
buffer->skb = skb;
|
||||
buffer->continuation = 0;
|
||||
|
||||
/* Pass off to hardware */
|
||||
falcon_push_buffers(tx_queue);
|
||||
|
||||
return NETDEV_TX_OK;
|
||||
|
||||
pci_err:
|
||||
EFX_ERR_RL(efx, " TX queue %d could not map skb with %d bytes %d "
|
||||
"fragments for DMA\n", tx_queue->queue, skb->len,
|
||||
skb_shinfo(skb)->nr_frags + 1);
|
||||
|
||||
/* Mark the packet as transmitted, and free the SKB ourselves */
|
||||
dev_kfree_skb_any((struct sk_buff *)skb);
|
||||
goto unwind;
|
||||
|
||||
stop:
|
||||
rc = NETDEV_TX_BUSY;
|
||||
|
||||
if (tx_queue->stopped == 1)
|
||||
efx_stop_queue(efx);
|
||||
|
||||
unwind:
|
||||
/* Work backwards until we hit the original insert pointer value */
|
||||
while (tx_queue->insert_count != tx_queue->write_count) {
|
||||
--tx_queue->insert_count;
|
||||
insert_ptr = tx_queue->insert_count & efx->type->txd_ring_mask;
|
||||
buffer = &tx_queue->buffer[insert_ptr];
|
||||
efx_dequeue_buffer(tx_queue, buffer);
|
||||
buffer->len = 0;
|
||||
}
|
||||
|
||||
/* Free the fragment we were mid-way through pushing */
|
||||
if (unmap_len)
|
||||
pci_unmap_page(pci_dev, unmap_addr, unmap_len,
|
||||
PCI_DMA_TODEVICE);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Remove packets from the TX queue
|
||||
*
|
||||
* This removes packets from the TX queue, up to and including the
|
||||
* specified index.
|
||||
*/
|
||||
static inline void efx_dequeue_buffers(struct efx_tx_queue *tx_queue,
|
||||
unsigned int index)
|
||||
{
|
||||
struct efx_nic *efx = tx_queue->efx;
|
||||
unsigned int stop_index, read_ptr;
|
||||
unsigned int mask = tx_queue->efx->type->txd_ring_mask;
|
||||
|
||||
stop_index = (index + 1) & mask;
|
||||
read_ptr = tx_queue->read_count & mask;
|
||||
|
||||
while (read_ptr != stop_index) {
|
||||
struct efx_tx_buffer *buffer = &tx_queue->buffer[read_ptr];
|
||||
if (unlikely(buffer->len == 0)) {
|
||||
EFX_ERR(tx_queue->efx, "TX queue %d spurious TX "
|
||||
"completion id %x\n", tx_queue->queue,
|
||||
read_ptr);
|
||||
efx_schedule_reset(efx, RESET_TYPE_TX_SKIP);
|
||||
return;
|
||||
}
|
||||
|
||||
efx_dequeue_buffer(tx_queue, buffer);
|
||||
buffer->continuation = 1;
|
||||
buffer->len = 0;
|
||||
|
||||
++tx_queue->read_count;
|
||||
read_ptr = tx_queue->read_count & mask;
|
||||
}
|
||||
}
|
||||
|
||||
/* Initiate a packet transmission on the specified TX queue.
|
||||
* Note that returning anything other than NETDEV_TX_OK will cause the
|
||||
* OS to free the skb.
|
||||
*
|
||||
* This function is split out from efx_hard_start_xmit to allow the
|
||||
* loopback test to direct packets via specific TX queues. It is
|
||||
* therefore a non-static inline, so as not to penalise performance
|
||||
* for non-loopback transmissions.
|
||||
*
|
||||
* Context: netif_tx_lock held
|
||||
*/
|
||||
inline int efx_xmit(struct efx_nic *efx,
|
||||
struct efx_tx_queue *tx_queue, struct sk_buff *skb)
|
||||
{
|
||||
int rc;
|
||||
|
||||
/* Map fragments for DMA and add to TX queue */
|
||||
rc = efx_enqueue_skb(tx_queue, skb);
|
||||
if (unlikely(rc != NETDEV_TX_OK))
|
||||
goto out;
|
||||
|
||||
/* Update last TX timer */
|
||||
efx->net_dev->trans_start = jiffies;
|
||||
|
||||
out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Initiate a packet transmission. We use one channel per CPU
|
||||
* (sharing when we have more CPUs than channels). On Falcon, the TX
|
||||
* completion events will be directed back to the CPU that transmitted
|
||||
* the packet, which should be cache-efficient.
|
||||
*
|
||||
* Context: non-blocking.
|
||||
* Note that returning anything other than NETDEV_TX_OK will cause the
|
||||
* OS to free the skb.
|
||||
*/
|
||||
int efx_hard_start_xmit(struct sk_buff *skb, struct net_device *net_dev)
|
||||
{
|
||||
struct efx_nic *efx = net_dev->priv;
|
||||
return efx_xmit(efx, &efx->tx_queue[0], skb);
|
||||
}
|
||||
|
||||
void efx_xmit_done(struct efx_tx_queue *tx_queue, unsigned int index)
|
||||
{
|
||||
unsigned fill_level;
|
||||
struct efx_nic *efx = tx_queue->efx;
|
||||
|
||||
EFX_BUG_ON_PARANOID(index > efx->type->txd_ring_mask);
|
||||
|
||||
efx_dequeue_buffers(tx_queue, index);
|
||||
|
||||
/* See if we need to restart the netif queue. This barrier
|
||||
* separates the update of read_count from the test of
|
||||
* stopped. */
|
||||
smp_mb();
|
||||
if (unlikely(tx_queue->stopped)) {
|
||||
fill_level = tx_queue->insert_count - tx_queue->read_count;
|
||||
if (fill_level < EFX_NETDEV_TX_THRESHOLD(tx_queue)) {
|
||||
EFX_BUG_ON_PARANOID(!NET_DEV_REGISTERED(efx));
|
||||
|
||||
/* Do this under netif_tx_lock(), to avoid racing
|
||||
* with efx_xmit(). */
|
||||
netif_tx_lock(efx->net_dev);
|
||||
if (tx_queue->stopped) {
|
||||
tx_queue->stopped = 0;
|
||||
efx_wake_queue(efx);
|
||||
}
|
||||
netif_tx_unlock(efx->net_dev);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int efx_probe_tx_queue(struct efx_tx_queue *tx_queue)
|
||||
{
|
||||
struct efx_nic *efx = tx_queue->efx;
|
||||
unsigned int txq_size;
|
||||
int i, rc;
|
||||
|
||||
EFX_LOG(efx, "creating TX queue %d\n", tx_queue->queue);
|
||||
|
||||
/* Allocate software ring */
|
||||
txq_size = (efx->type->txd_ring_mask + 1) * sizeof(*tx_queue->buffer);
|
||||
tx_queue->buffer = kzalloc(txq_size, GFP_KERNEL);
|
||||
if (!tx_queue->buffer) {
|
||||
rc = -ENOMEM;
|
||||
goto fail1;
|
||||
}
|
||||
for (i = 0; i <= efx->type->txd_ring_mask; ++i)
|
||||
tx_queue->buffer[i].continuation = 1;
|
||||
|
||||
/* Allocate hardware ring */
|
||||
rc = falcon_probe_tx(tx_queue);
|
||||
if (rc)
|
||||
goto fail2;
|
||||
|
||||
return 0;
|
||||
|
||||
fail2:
|
||||
kfree(tx_queue->buffer);
|
||||
tx_queue->buffer = NULL;
|
||||
fail1:
|
||||
tx_queue->used = 0;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int efx_init_tx_queue(struct efx_tx_queue *tx_queue)
|
||||
{
|
||||
EFX_LOG(tx_queue->efx, "initialising TX queue %d\n", tx_queue->queue);
|
||||
|
||||
tx_queue->insert_count = 0;
|
||||
tx_queue->write_count = 0;
|
||||
tx_queue->read_count = 0;
|
||||
tx_queue->old_read_count = 0;
|
||||
BUG_ON(tx_queue->stopped);
|
||||
|
||||
/* Set up TX descriptor ring */
|
||||
return falcon_init_tx(tx_queue);
|
||||
}
|
||||
|
||||
void efx_release_tx_buffers(struct efx_tx_queue *tx_queue)
|
||||
{
|
||||
struct efx_tx_buffer *buffer;
|
||||
|
||||
if (!tx_queue->buffer)
|
||||
return;
|
||||
|
||||
/* Free any buffers left in the ring */
|
||||
while (tx_queue->read_count != tx_queue->write_count) {
|
||||
buffer = &tx_queue->buffer[tx_queue->read_count &
|
||||
tx_queue->efx->type->txd_ring_mask];
|
||||
efx_dequeue_buffer(tx_queue, buffer);
|
||||
buffer->continuation = 1;
|
||||
buffer->len = 0;
|
||||
|
||||
++tx_queue->read_count;
|
||||
}
|
||||
}
|
||||
|
||||
void efx_fini_tx_queue(struct efx_tx_queue *tx_queue)
|
||||
{
|
||||
EFX_LOG(tx_queue->efx, "shutting down TX queue %d\n", tx_queue->queue);
|
||||
|
||||
/* Flush TX queue, remove descriptor ring */
|
||||
falcon_fini_tx(tx_queue);
|
||||
|
||||
efx_release_tx_buffers(tx_queue);
|
||||
|
||||
/* Release queue's stop on port, if any */
|
||||
if (tx_queue->stopped) {
|
||||
tx_queue->stopped = 0;
|
||||
efx_wake_queue(tx_queue->efx);
|
||||
}
|
||||
}
|
||||
|
||||
void efx_remove_tx_queue(struct efx_tx_queue *tx_queue)
|
||||
{
|
||||
EFX_LOG(tx_queue->efx, "destroying TX queue %d\n", tx_queue->queue);
|
||||
falcon_remove_tx(tx_queue);
|
||||
|
||||
kfree(tx_queue->buffer);
|
||||
tx_queue->buffer = NULL;
|
||||
tx_queue->used = 0;
|
||||
}
|
||||
|
||||
|
24
drivers/net/sfc/tx.h
Normal file
24
drivers/net/sfc/tx.h
Normal file
@ -0,0 +1,24 @@
|
||||
/****************************************************************************
|
||||
* Driver for Solarflare Solarstorm network controllers and boards
|
||||
* Copyright 2006 Fen Systems Ltd.
|
||||
* Copyright 2006-2008 Solarflare Communications Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published
|
||||
* by the Free Software Foundation, incorporated herein by reference.
|
||||
*/
|
||||
|
||||
#ifndef EFX_TX_H
|
||||
#define EFX_TX_H
|
||||
|
||||
#include "net_driver.h"
|
||||
|
||||
int efx_probe_tx_queue(struct efx_tx_queue *tx_queue);
|
||||
void efx_remove_tx_queue(struct efx_tx_queue *tx_queue);
|
||||
int efx_init_tx_queue(struct efx_tx_queue *tx_queue);
|
||||
void efx_fini_tx_queue(struct efx_tx_queue *tx_queue);
|
||||
|
||||
int efx_hard_start_xmit(struct sk_buff *skb, struct net_device *net_dev);
|
||||
void efx_release_tx_buffers(struct efx_tx_queue *tx_queue);
|
||||
|
||||
#endif /* EFX_TX_H */
|
56
drivers/net/sfc/workarounds.h
Normal file
56
drivers/net/sfc/workarounds.h
Normal file
@ -0,0 +1,56 @@
|
||||
/****************************************************************************
|
||||
* Driver for Solarflare Solarstorm network controllers and boards
|
||||
* Copyright 2006-2008 Solarflare Communications Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published
|
||||
* by the Free Software Foundation, incorporated herein by reference.
|
||||
*/
|
||||
|
||||
#ifndef EFX_WORKAROUNDS_H
|
||||
#define EFX_WORKAROUNDS_H
|
||||
|
||||
/*
|
||||
* Hardware workarounds.
|
||||
* Bug numbers are from Solarflare's Bugzilla.
|
||||
*/
|
||||
|
||||
#define EFX_WORKAROUND_ALWAYS(efx) 1
|
||||
#define EFX_WORKAROUND_FALCON_A(efx) (FALCON_REV(efx) <= FALCON_REV_A1)
|
||||
|
||||
/* XAUI resets if link not detected */
|
||||
#define EFX_WORKAROUND_5147 EFX_WORKAROUND_ALWAYS
|
||||
/* SNAP frames have TOBE_DISC set */
|
||||
#define EFX_WORKAROUND_5475 EFX_WORKAROUND_ALWAYS
|
||||
/* RX PCIe double split performance issue */
|
||||
#define EFX_WORKAROUND_7575 EFX_WORKAROUND_ALWAYS
|
||||
/* TX pkt parser problem with <= 16 byte TXes */
|
||||
#define EFX_WORKAROUND_9141 EFX_WORKAROUND_ALWAYS
|
||||
/* XGXS and XAUI reset sequencing in SW */
|
||||
#define EFX_WORKAROUND_9388 EFX_WORKAROUND_ALWAYS
|
||||
/* Low rate CRC errors require XAUI reset */
|
||||
#define EFX_WORKAROUND_10750 EFX_WORKAROUND_ALWAYS
|
||||
/* TX_EV_PKT_ERR can be caused by a dangling TX descriptor
|
||||
* or a PCIe error (bug 11028) */
|
||||
#define EFX_WORKAROUND_10727 EFX_WORKAROUND_ALWAYS
|
||||
/* Transmit flow control may get disabled */
|
||||
#define EFX_WORKAROUND_11482 EFX_WORKAROUND_ALWAYS
|
||||
/* Flush events can take a very long time to appear */
|
||||
#define EFX_WORKAROUND_11557 EFX_WORKAROUND_ALWAYS
|
||||
|
||||
/* Spurious parity errors in TSORT buffers */
|
||||
#define EFX_WORKAROUND_5129 EFX_WORKAROUND_FALCON_A
|
||||
/* iSCSI parsing errors */
|
||||
#define EFX_WORKAROUND_5583 EFX_WORKAROUND_FALCON_A
|
||||
/* RX events go missing */
|
||||
#define EFX_WORKAROUND_5676 EFX_WORKAROUND_FALCON_A
|
||||
/* RX_RESET on A1 */
|
||||
#define EFX_WORKAROUND_6555 EFX_WORKAROUND_FALCON_A
|
||||
/* Increase filter depth to avoid RX_RESET */
|
||||
#define EFX_WORKAROUND_7244 EFX_WORKAROUND_FALCON_A
|
||||
/* Flushes may never complete */
|
||||
#define EFX_WORKAROUND_7803 EFX_WORKAROUND_FALCON_A
|
||||
/* Leak overlength packets rather than free */
|
||||
#define EFX_WORKAROUND_8071 EFX_WORKAROUND_FALCON_A
|
||||
|
||||
#endif /* EFX_WORKAROUNDS_H */
|
62
drivers/net/sfc/xenpack.h
Normal file
62
drivers/net/sfc/xenpack.h
Normal file
@ -0,0 +1,62 @@
|
||||
/****************************************************************************
|
||||
* Driver for Solarflare Solarstorm network controllers and boards
|
||||
* Copyright 2006 Solarflare Communications Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published
|
||||
* by the Free Software Foundation, incorporated herein by reference.
|
||||
*/
|
||||
|
||||
#ifndef EFX_XENPACK_H
|
||||
#define EFX_XENPACK_H
|
||||
|
||||
/* Exported functions from Xenpack standard PHY control */
|
||||
|
||||
#include "mdio_10g.h"
|
||||
|
||||
/****************************************************************************/
|
||||
/* XENPACK MDIO register extensions */
|
||||
#define MDIO_XP_LASI_RX_CTRL (0x9000)
|
||||
#define MDIO_XP_LASI_TX_CTRL (0x9001)
|
||||
#define MDIO_XP_LASI_CTRL (0x9002)
|
||||
#define MDIO_XP_LASI_RX_STAT (0x9003)
|
||||
#define MDIO_XP_LASI_TX_STAT (0x9004)
|
||||
#define MDIO_XP_LASI_STAT (0x9005)
|
||||
|
||||
/* Control/Status bits */
|
||||
#define XP_LASI_LS_ALARM (1 << 0)
|
||||
#define XP_LASI_TX_ALARM (1 << 1)
|
||||
#define XP_LASI_RX_ALARM (1 << 2)
|
||||
/* These two are Quake vendor extensions to the standard XENPACK defines */
|
||||
#define XP_LASI_LS_INTB (1 << 3)
|
||||
#define XP_LASI_TEST (1 << 7)
|
||||
|
||||
/* Enable LASI interrupts for PHY */
|
||||
static inline void xenpack_enable_lasi_irqs(struct efx_nic *efx)
|
||||
{
|
||||
int reg;
|
||||
int phy_id = efx->mii.phy_id;
|
||||
/* Read to clear LASI status register */
|
||||
reg = mdio_clause45_read(efx, phy_id, MDIO_MMD_PMAPMD,
|
||||
MDIO_XP_LASI_STAT);
|
||||
|
||||
mdio_clause45_write(efx, phy_id, MDIO_MMD_PMAPMD,
|
||||
MDIO_XP_LASI_CTRL, XP_LASI_LS_ALARM);
|
||||
}
|
||||
|
||||
/* Read the LASI interrupt status to clear the interrupt. */
|
||||
static inline int xenpack_clear_lasi_irqs(struct efx_nic *efx)
|
||||
{
|
||||
/* Read to clear link status alarm */
|
||||
return mdio_clause45_read(efx, efx->mii.phy_id,
|
||||
MDIO_MMD_PMAPMD, MDIO_XP_LASI_STAT);
|
||||
}
|
||||
|
||||
/* Turn off LASI interrupts */
|
||||
static inline void xenpack_disable_lasi_irqs(struct efx_nic *efx)
|
||||
{
|
||||
mdio_clause45_write(efx, efx->mii.phy_id, MDIO_MMD_PMAPMD,
|
||||
MDIO_XP_LASI_CTRL, 0);
|
||||
}
|
||||
|
||||
#endif /* EFX_XENPACK_H */
|
132
drivers/net/sfc/xfp_phy.c
Normal file
132
drivers/net/sfc/xfp_phy.c
Normal file
@ -0,0 +1,132 @@
|
||||
/****************************************************************************
|
||||
* Driver for Solarflare Solarstorm network controllers and boards
|
||||
* Copyright 2006-2008 Solarflare Communications Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published
|
||||
* by the Free Software Foundation, incorporated herein by reference.
|
||||
*/
|
||||
/*
|
||||
* Driver for XFP optical PHYs (plus some support specific to the Quake 2032)
|
||||
* See www.amcc.com for details (search for qt2032)
|
||||
*/
|
||||
|
||||
#include <linux/timer.h>
|
||||
#include <linux/delay.h>
|
||||
#include "efx.h"
|
||||
#include "gmii.h"
|
||||
#include "mdio_10g.h"
|
||||
#include "xenpack.h"
|
||||
#include "phy.h"
|
||||
#include "mac.h"
|
||||
|
||||
#define XFP_REQUIRED_DEVS (MDIO_MMDREG_DEVS0_PCS | \
|
||||
MDIO_MMDREG_DEVS0_PMAPMD | \
|
||||
MDIO_MMDREG_DEVS0_PHYXS)
|
||||
|
||||
/****************************************************************************/
|
||||
/* Quake-specific MDIO registers */
|
||||
#define MDIO_QUAKE_LED0_REG (0xD006)
|
||||
|
||||
void xfp_set_led(struct efx_nic *p, int led, int mode)
|
||||
{
|
||||
int addr = MDIO_QUAKE_LED0_REG + led;
|
||||
mdio_clause45_write(p, p->mii.phy_id, MDIO_MMD_PMAPMD, addr,
|
||||
mode);
|
||||
}
|
||||
|
||||
#define XFP_MAX_RESET_TIME 500
|
||||
#define XFP_RESET_WAIT 10
|
||||
|
||||
/* Reset the PHYXS MMD. This is documented (for the Quake PHY) as doing
|
||||
* a complete soft reset.
|
||||
*/
|
||||
static int xfp_reset_phy(struct efx_nic *efx)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = mdio_clause45_reset_mmd(efx, MDIO_MMD_PHYXS,
|
||||
XFP_MAX_RESET_TIME / XFP_RESET_WAIT,
|
||||
XFP_RESET_WAIT);
|
||||
if (rc < 0)
|
||||
goto fail;
|
||||
|
||||
/* Wait 250ms for the PHY to complete bootup */
|
||||
msleep(250);
|
||||
|
||||
/* Check that all the MMDs we expect are present and responding. We
|
||||
* expect faults on some if the link is down, but not on the PHY XS */
|
||||
rc = mdio_clause45_check_mmds(efx, XFP_REQUIRED_DEVS,
|
||||
MDIO_MMDREG_DEVS0_PHYXS);
|
||||
if (rc < 0)
|
||||
goto fail;
|
||||
|
||||
efx->board_info.init_leds(efx);
|
||||
|
||||
return rc;
|
||||
|
||||
fail:
|
||||
EFX_ERR(efx, "XFP: reset timed out!\n");
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int xfp_phy_init(struct efx_nic *efx)
|
||||
{
|
||||
u32 devid = mdio_clause45_read_id(efx, MDIO_MMD_PHYXS);
|
||||
int rc;
|
||||
|
||||
EFX_INFO(efx, "XFP: PHY ID reg %x (OUI %x model %x revision"
|
||||
" %x)\n", devid, MDIO_ID_OUI(devid), MDIO_ID_MODEL(devid),
|
||||
MDIO_ID_REV(devid));
|
||||
|
||||
rc = xfp_reset_phy(efx);
|
||||
|
||||
EFX_INFO(efx, "XFP: PHY init %s.\n",
|
||||
rc ? "failed" : "successful");
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void xfp_phy_clear_interrupt(struct efx_nic *efx)
|
||||
{
|
||||
xenpack_clear_lasi_irqs(efx);
|
||||
}
|
||||
|
||||
static int xfp_link_ok(struct efx_nic *efx)
|
||||
{
|
||||
return mdio_clause45_links_ok(efx, XFP_REQUIRED_DEVS);
|
||||
}
|
||||
|
||||
static int xfp_phy_check_hw(struct efx_nic *efx)
|
||||
{
|
||||
int rc = 0;
|
||||
int link_up = xfp_link_ok(efx);
|
||||
/* Simulate a PHY event if link state has changed */
|
||||
if (link_up != efx->link_up)
|
||||
falcon_xmac_sim_phy_event(efx);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void xfp_phy_reconfigure(struct efx_nic *efx)
|
||||
{
|
||||
efx->link_up = xfp_link_ok(efx);
|
||||
efx->link_options = GM_LPA_10000FULL;
|
||||
}
|
||||
|
||||
|
||||
static void xfp_phy_fini(struct efx_nic *efx)
|
||||
{
|
||||
/* Clobber the LED if it was blinking */
|
||||
efx->board_info.blink(efx, 0);
|
||||
}
|
||||
|
||||
struct efx_phy_operations falcon_xfp_phy_ops = {
|
||||
.init = xfp_phy_init,
|
||||
.reconfigure = xfp_phy_reconfigure,
|
||||
.check_hw = xfp_phy_check_hw,
|
||||
.fini = xfp_phy_fini,
|
||||
.clear_interrupt = xfp_phy_clear_interrupt,
|
||||
.reset_xaui = efx_port_dummy_op_void,
|
||||
.mmds = XFP_REQUIRED_DEVS,
|
||||
};
|
@ -212,6 +212,12 @@ enum _DescStatusBit {
|
||||
THOL2 = 0x20000000,
|
||||
THOL1 = 0x10000000,
|
||||
THOL0 = 0x00000000,
|
||||
|
||||
WND = 0x00080000,
|
||||
TABRT = 0x00040000,
|
||||
FIFO = 0x00020000,
|
||||
LINK = 0x00010000,
|
||||
ColCountMask = 0x0000ffff,
|
||||
/* RxDesc.status */
|
||||
IPON = 0x20000000,
|
||||
TCPON = 0x10000000,
|
||||
@ -480,30 +486,23 @@ static inline void sis190_make_unusable_by_asic(struct RxDesc *desc)
|
||||
desc->status = 0x0;
|
||||
}
|
||||
|
||||
static int sis190_alloc_rx_skb(struct pci_dev *pdev, struct sk_buff **sk_buff,
|
||||
struct RxDesc *desc, u32 rx_buf_sz)
|
||||
static struct sk_buff *sis190_alloc_rx_skb(struct sis190_private *tp,
|
||||
struct RxDesc *desc)
|
||||
{
|
||||
u32 rx_buf_sz = tp->rx_buf_sz;
|
||||
struct sk_buff *skb;
|
||||
|
||||
skb = netdev_alloc_skb(tp->dev, rx_buf_sz);
|
||||
if (likely(skb)) {
|
||||
dma_addr_t mapping;
|
||||
int ret = 0;
|
||||
|
||||
skb = dev_alloc_skb(rx_buf_sz);
|
||||
if (!skb)
|
||||
goto err_out;
|
||||
|
||||
*sk_buff = skb;
|
||||
|
||||
mapping = pci_map_single(pdev, skb->data, rx_buf_sz,
|
||||
mapping = pci_map_single(tp->pci_dev, skb->data, tp->rx_buf_sz,
|
||||
PCI_DMA_FROMDEVICE);
|
||||
|
||||
sis190_map_to_asic(desc, mapping, rx_buf_sz);
|
||||
out:
|
||||
return ret;
|
||||
|
||||
err_out:
|
||||
ret = -ENOMEM;
|
||||
} else
|
||||
sis190_make_unusable_by_asic(desc);
|
||||
goto out;
|
||||
|
||||
return skb;
|
||||
}
|
||||
|
||||
static u32 sis190_rx_fill(struct sis190_private *tp, struct net_device *dev,
|
||||
@ -512,37 +511,41 @@ static u32 sis190_rx_fill(struct sis190_private *tp, struct net_device *dev,
|
||||
u32 cur;
|
||||
|
||||
for (cur = start; cur < end; cur++) {
|
||||
int ret, i = cur % NUM_RX_DESC;
|
||||
unsigned int i = cur % NUM_RX_DESC;
|
||||
|
||||
if (tp->Rx_skbuff[i])
|
||||
continue;
|
||||
|
||||
ret = sis190_alloc_rx_skb(tp->pci_dev, tp->Rx_skbuff + i,
|
||||
tp->RxDescRing + i, tp->rx_buf_sz);
|
||||
if (ret < 0)
|
||||
tp->Rx_skbuff[i] = sis190_alloc_rx_skb(tp, tp->RxDescRing + i);
|
||||
|
||||
if (!tp->Rx_skbuff[i])
|
||||
break;
|
||||
}
|
||||
return cur - start;
|
||||
}
|
||||
|
||||
static inline int sis190_try_rx_copy(struct sk_buff **sk_buff, int pkt_size,
|
||||
struct RxDesc *desc, int rx_buf_sz)
|
||||
static bool sis190_try_rx_copy(struct sis190_private *tp,
|
||||
struct sk_buff **sk_buff, int pkt_size,
|
||||
dma_addr_t addr)
|
||||
{
|
||||
int ret = -1;
|
||||
|
||||
if (pkt_size < rx_copybreak) {
|
||||
struct sk_buff *skb;
|
||||
bool done = false;
|
||||
|
||||
skb = dev_alloc_skb(pkt_size + NET_IP_ALIGN);
|
||||
if (skb) {
|
||||
skb_reserve(skb, NET_IP_ALIGN);
|
||||
if (pkt_size >= rx_copybreak)
|
||||
goto out;
|
||||
|
||||
skb = netdev_alloc_skb(tp->dev, pkt_size + 2);
|
||||
if (!skb)
|
||||
goto out;
|
||||
|
||||
pci_dma_sync_single_for_device(tp->pci_dev, addr, pkt_size,
|
||||
PCI_DMA_FROMDEVICE);
|
||||
skb_reserve(skb, 2);
|
||||
skb_copy_to_linear_data(skb, sk_buff[0]->data, pkt_size);
|
||||
*sk_buff = skb;
|
||||
sis190_give_to_asic(desc, rx_buf_sz);
|
||||
ret = 0;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
done = true;
|
||||
out:
|
||||
return done;
|
||||
}
|
||||
|
||||
static inline int sis190_rx_pkt_err(u32 status, struct net_device_stats *stats)
|
||||
@ -592,9 +595,9 @@ static int sis190_rx_interrupt(struct net_device *dev,
|
||||
sis190_give_to_asic(desc, tp->rx_buf_sz);
|
||||
else {
|
||||
struct sk_buff *skb = tp->Rx_skbuff[entry];
|
||||
dma_addr_t addr = le32_to_cpu(desc->addr);
|
||||
int pkt_size = (status & RxSizeMask) - 4;
|
||||
void (*pci_action)(struct pci_dev *, dma_addr_t,
|
||||
size_t, int) = pci_dma_sync_single_for_device;
|
||||
struct pci_dev *pdev = tp->pci_dev;
|
||||
|
||||
if (unlikely(pkt_size > tp->rx_buf_sz)) {
|
||||
net_intr(tp, KERN_INFO
|
||||
@ -606,20 +609,18 @@ static int sis190_rx_interrupt(struct net_device *dev,
|
||||
continue;
|
||||
}
|
||||
|
||||
pci_dma_sync_single_for_cpu(tp->pci_dev,
|
||||
le32_to_cpu(desc->addr), tp->rx_buf_sz,
|
||||
PCI_DMA_FROMDEVICE);
|
||||
|
||||
if (sis190_try_rx_copy(&skb, pkt_size, desc,
|
||||
tp->rx_buf_sz)) {
|
||||
pci_action = pci_unmap_single;
|
||||
if (sis190_try_rx_copy(tp, &skb, pkt_size, addr)) {
|
||||
pci_dma_sync_single_for_device(pdev, addr,
|
||||
tp->rx_buf_sz, PCI_DMA_FROMDEVICE);
|
||||
sis190_give_to_asic(desc, tp->rx_buf_sz);
|
||||
} else {
|
||||
pci_unmap_single(pdev, addr, tp->rx_buf_sz,
|
||||
PCI_DMA_FROMDEVICE);
|
||||
tp->Rx_skbuff[entry] = NULL;
|
||||
sis190_make_unusable_by_asic(desc);
|
||||
}
|
||||
|
||||
pci_action(tp->pci_dev, le32_to_cpu(desc->addr),
|
||||
tp->rx_buf_sz, PCI_DMA_FROMDEVICE);
|
||||
|
||||
skb_put(skb, pkt_size);
|
||||
skb->protocol = eth_type_trans(skb, dev);
|
||||
|
||||
@ -658,9 +659,31 @@ static void sis190_unmap_tx_skb(struct pci_dev *pdev, struct sk_buff *skb,
|
||||
memset(desc, 0x00, sizeof(*desc));
|
||||
}
|
||||
|
||||
static inline int sis190_tx_pkt_err(u32 status, struct net_device_stats *stats)
|
||||
{
|
||||
#define TxErrMask (WND | TABRT | FIFO | LINK)
|
||||
|
||||
if (!unlikely(status & TxErrMask))
|
||||
return 0;
|
||||
|
||||
if (status & WND)
|
||||
stats->tx_window_errors++;
|
||||
if (status & TABRT)
|
||||
stats->tx_aborted_errors++;
|
||||
if (status & FIFO)
|
||||
stats->tx_fifo_errors++;
|
||||
if (status & LINK)
|
||||
stats->tx_carrier_errors++;
|
||||
|
||||
stats->tx_errors++;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void sis190_tx_interrupt(struct net_device *dev,
|
||||
struct sis190_private *tp, void __iomem *ioaddr)
|
||||
{
|
||||
struct net_device_stats *stats = &dev->stats;
|
||||
u32 pending, dirty_tx = tp->dirty_tx;
|
||||
/*
|
||||
* It would not be needed if queueing was allowed to be enabled
|
||||
@ -675,15 +698,19 @@ static void sis190_tx_interrupt(struct net_device *dev,
|
||||
for (; pending; pending--, dirty_tx++) {
|
||||
unsigned int entry = dirty_tx % NUM_TX_DESC;
|
||||
struct TxDesc *txd = tp->TxDescRing + entry;
|
||||
u32 status = le32_to_cpu(txd->status);
|
||||
struct sk_buff *skb;
|
||||
|
||||
if (le32_to_cpu(txd->status) & OWNbit)
|
||||
if (status & OWNbit)
|
||||
break;
|
||||
|
||||
skb = tp->Tx_skbuff[entry];
|
||||
|
||||
dev->stats.tx_packets++;
|
||||
dev->stats.tx_bytes += skb->len;
|
||||
if (likely(sis190_tx_pkt_err(status, stats) == 0)) {
|
||||
stats->tx_packets++;
|
||||
stats->tx_bytes += skb->len;
|
||||
stats->collisions += ((status & ColCountMask) - 1);
|
||||
}
|
||||
|
||||
sis190_unmap_tx_skb(tp->pci_dev, skb, txd);
|
||||
tp->Tx_skbuff[entry] = NULL;
|
||||
@ -904,10 +931,9 @@ static void sis190_phy_task(struct work_struct *work)
|
||||
mod_timer(&tp->timer, jiffies + HZ/10);
|
||||
} else if (!(mdio_read_latched(ioaddr, phy_id, MII_BMSR) &
|
||||
BMSR_ANEGCOMPLETE)) {
|
||||
net_link(tp, KERN_WARNING "%s: PHY reset until link up.\n",
|
||||
dev->name);
|
||||
netif_carrier_off(dev);
|
||||
mdio_write(ioaddr, phy_id, MII_BMCR, val | BMCR_RESET);
|
||||
net_link(tp, KERN_WARNING "%s: auto-negotiating...\n",
|
||||
dev->name);
|
||||
mod_timer(&tp->timer, jiffies + SIS190_PHY_TIMEOUT);
|
||||
} else {
|
||||
/* Rejoice ! */
|
||||
|
@ -6,6 +6,10 @@ config IWLCORE
|
||||
tristate "Intel Wireless Wifi Core"
|
||||
depends on PCI && MAC80211 && WLAN_80211 && EXPERIMENTAL
|
||||
select IWLWIFI
|
||||
select MAC80211_LEDS if IWLWIFI_LEDS
|
||||
select LEDS_CLASS if IWLWIFI_LEDS
|
||||
select RFKILL if IWLWIFI_RFKILL
|
||||
select RFKILL_INPUT if IWLWIFI_RFKILL
|
||||
|
||||
config IWLWIFI_LEDS
|
||||
bool
|
||||
@ -14,8 +18,6 @@ config IWLWIFI_LEDS
|
||||
config IWLWIFI_RFKILL
|
||||
boolean "IWLWIFI RF kill support"
|
||||
depends on IWLCORE
|
||||
select RFKILL
|
||||
select RFKILL_INPUT
|
||||
|
||||
config IWL4965
|
||||
tristate "Intel Wireless WiFi 4965AGN"
|
||||
@ -55,8 +57,6 @@ config IWL4965_HT
|
||||
config IWL4965_LEDS
|
||||
bool "Enable LEDS features in iwl4965 driver"
|
||||
depends on IWL4965
|
||||
select MAC80211_LEDS
|
||||
select LEDS_CLASS
|
||||
select IWLWIFI_LEDS
|
||||
---help---
|
||||
This option enables LEDS for the iwlwifi drivers
|
||||
@ -112,6 +112,8 @@ config IWL3945
|
||||
depends on PCI && MAC80211 && WLAN_80211 && EXPERIMENTAL
|
||||
select FW_LOADER
|
||||
select IWLWIFI
|
||||
select MAC80211_LEDS if IWL3945_LEDS
|
||||
select LEDS_CLASS if IWL3945_LEDS
|
||||
---help---
|
||||
Select to build the driver supporting the:
|
||||
|
||||
@ -143,8 +145,6 @@ config IWL3945_SPECTRUM_MEASUREMENT
|
||||
config IWL3945_LEDS
|
||||
bool "Enable LEDS features in iwl3945 driver"
|
||||
depends on IWL3945
|
||||
select MAC80211_LEDS
|
||||
select LEDS_CLASS
|
||||
---help---
|
||||
This option enables LEDS for the iwl3945 driver.
|
||||
|
||||
|
@ -152,44 +152,89 @@ __ccwgroup_create_symlinks(struct ccwgroup_device *gdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __get_next_bus_id(const char **buf, char *bus_id)
|
||||
{
|
||||
int rc, len;
|
||||
char *start, *end;
|
||||
|
||||
start = (char *)*buf;
|
||||
end = strchr(start, ',');
|
||||
if (!end) {
|
||||
/* Last entry. Strip trailing newline, if applicable. */
|
||||
end = strchr(start, '\n');
|
||||
if (end)
|
||||
*end = '\0';
|
||||
len = strlen(start) + 1;
|
||||
} else {
|
||||
len = end - start + 1;
|
||||
end++;
|
||||
}
|
||||
if (len < BUS_ID_SIZE) {
|
||||
strlcpy(bus_id, start, len);
|
||||
rc = 0;
|
||||
} else
|
||||
rc = -EINVAL;
|
||||
*buf = end;
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int __is_valid_bus_id(char bus_id[BUS_ID_SIZE])
|
||||
{
|
||||
int cssid, ssid, devno;
|
||||
|
||||
/* Must be of form %x.%x.%04x */
|
||||
if (sscanf(bus_id, "%x.%1x.%04x", &cssid, &ssid, &devno) != 3)
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* ccwgroup_create() - create and register a ccw group device
|
||||
* ccwgroup_create_from_string() - create and register a ccw group device
|
||||
* @root: parent device for the new device
|
||||
* @creator_id: identifier of creating driver
|
||||
* @cdrv: ccw driver of slave devices
|
||||
* @argc: number of slave devices
|
||||
* @argv: bus ids of slave devices
|
||||
* @num_devices: number of slave devices
|
||||
* @buf: buffer containing comma separated bus ids of slave devices
|
||||
*
|
||||
* Create and register a new ccw group device as a child of @root. Slave
|
||||
* devices are obtained from the list of bus ids given in @argv[] and must all
|
||||
* devices are obtained from the list of bus ids given in @buf and must all
|
||||
* belong to @cdrv.
|
||||
* Returns:
|
||||
* %0 on success and an error code on failure.
|
||||
* Context:
|
||||
* non-atomic
|
||||
*/
|
||||
int ccwgroup_create(struct device *root, unsigned int creator_id,
|
||||
struct ccw_driver *cdrv, int argc, char *argv[])
|
||||
int ccwgroup_create_from_string(struct device *root, unsigned int creator_id,
|
||||
struct ccw_driver *cdrv, int num_devices,
|
||||
const char *buf)
|
||||
{
|
||||
struct ccwgroup_device *gdev;
|
||||
int i;
|
||||
int rc;
|
||||
int rc, i;
|
||||
char tmp_bus_id[BUS_ID_SIZE];
|
||||
const char *curr_buf;
|
||||
|
||||
if (argc > 256) /* disallow dumb users */
|
||||
return -EINVAL;
|
||||
|
||||
gdev = kzalloc(sizeof(*gdev) + argc*sizeof(gdev->cdev[0]), GFP_KERNEL);
|
||||
gdev = kzalloc(sizeof(*gdev) + num_devices * sizeof(gdev->cdev[0]),
|
||||
GFP_KERNEL);
|
||||
if (!gdev)
|
||||
return -ENOMEM;
|
||||
|
||||
atomic_set(&gdev->onoff, 0);
|
||||
mutex_init(&gdev->reg_mutex);
|
||||
mutex_lock(&gdev->reg_mutex);
|
||||
for (i = 0; i < argc; i++) {
|
||||
gdev->cdev[i] = get_ccwdev_by_busid(cdrv, argv[i]);
|
||||
|
||||
/* all devices have to be of the same type in
|
||||
* order to be grouped */
|
||||
curr_buf = buf;
|
||||
for (i = 0; i < num_devices && curr_buf; i++) {
|
||||
rc = __get_next_bus_id(&curr_buf, tmp_bus_id);
|
||||
if (rc != 0)
|
||||
goto error;
|
||||
if (!__is_valid_bus_id(tmp_bus_id)) {
|
||||
rc = -EINVAL;
|
||||
goto error;
|
||||
}
|
||||
gdev->cdev[i] = get_ccwdev_by_busid(cdrv, tmp_bus_id);
|
||||
/*
|
||||
* All devices have to be of the same type in
|
||||
* order to be grouped.
|
||||
*/
|
||||
if (!gdev->cdev[i]
|
||||
|| gdev->cdev[i]->id.driver_info !=
|
||||
gdev->cdev[0]->id.driver_info) {
|
||||
@ -203,9 +248,18 @@ int ccwgroup_create(struct device *root, unsigned int creator_id,
|
||||
}
|
||||
dev_set_drvdata(&gdev->cdev[i]->dev, gdev);
|
||||
}
|
||||
|
||||
/* Check for sufficient number of bus ids. */
|
||||
if (i < num_devices && !curr_buf) {
|
||||
rc = -EINVAL;
|
||||
goto error;
|
||||
}
|
||||
/* Check for trailing stuff. */
|
||||
if (i == num_devices && strlen(curr_buf) > 0) {
|
||||
rc = -EINVAL;
|
||||
goto error;
|
||||
}
|
||||
gdev->creator_id = creator_id;
|
||||
gdev->count = argc;
|
||||
gdev->count = num_devices;
|
||||
gdev->dev.bus = &ccwgroup_bus_type;
|
||||
gdev->dev.parent = root;
|
||||
gdev->dev.release = ccwgroup_release;
|
||||
@ -233,7 +287,7 @@ int ccwgroup_create(struct device *root, unsigned int creator_id,
|
||||
device_remove_file(&gdev->dev, &dev_attr_ungroup);
|
||||
device_unregister(&gdev->dev);
|
||||
error:
|
||||
for (i = 0; i < argc; i++)
|
||||
for (i = 0; i < num_devices; i++)
|
||||
if (gdev->cdev[i]) {
|
||||
if (dev_get_drvdata(&gdev->cdev[i]->dev) == gdev)
|
||||
dev_set_drvdata(&gdev->cdev[i]->dev, NULL);
|
||||
@ -243,6 +297,7 @@ error:
|
||||
put_device(&gdev->dev);
|
||||
return rc;
|
||||
}
|
||||
EXPORT_SYMBOL(ccwgroup_create_from_string);
|
||||
|
||||
static int __init
|
||||
init_ccwgroup (void)
|
||||
@ -521,6 +576,5 @@ void ccwgroup_remove_ccwdev(struct ccw_device *cdev)
|
||||
MODULE_LICENSE("GPL");
|
||||
EXPORT_SYMBOL(ccwgroup_driver_register);
|
||||
EXPORT_SYMBOL(ccwgroup_driver_unregister);
|
||||
EXPORT_SYMBOL(ccwgroup_create);
|
||||
EXPORT_SYMBOL(ccwgroup_probe_ccwdev);
|
||||
EXPORT_SYMBOL(ccwgroup_remove_ccwdev);
|
||||
|
@ -62,30 +62,14 @@ static struct device *cu3088_root_dev;
|
||||
static ssize_t
|
||||
group_write(struct device_driver *drv, const char *buf, size_t count)
|
||||
{
|
||||
const char *start, *end;
|
||||
char bus_ids[2][BUS_ID_SIZE], *argv[2];
|
||||
int i;
|
||||
int ret;
|
||||
struct ccwgroup_driver *cdrv;
|
||||
|
||||
cdrv = to_ccwgroupdrv(drv);
|
||||
if (!cdrv)
|
||||
return -EINVAL;
|
||||
start = buf;
|
||||
for (i=0; i<2; i++) {
|
||||
static const char delim[] = {',', '\n'};
|
||||
int len;
|
||||
|
||||
if (!(end = strchr(start, delim[i])))
|
||||
return -EINVAL;
|
||||
len = min_t(ptrdiff_t, BUS_ID_SIZE, end - start + 1);
|
||||
strlcpy (bus_ids[i], start, len);
|
||||
argv[i] = bus_ids[i];
|
||||
start = end + 1;
|
||||
}
|
||||
|
||||
ret = ccwgroup_create(cu3088_root_dev, cdrv->driver_id,
|
||||
&cu3088_driver, 2, argv);
|
||||
ret = ccwgroup_create_from_string(cu3088_root_dev, cdrv->driver_id,
|
||||
&cu3088_driver, 2, buf);
|
||||
|
||||
return (ret == 0) ? count : ret;
|
||||
}
|
||||
|
@ -1793,6 +1793,7 @@ lcs_get_skb(struct lcs_card *card, char *skb_data, unsigned int skb_len)
|
||||
skb->protocol = card->lan_type_trans(skb, card->dev);
|
||||
card->stats.rx_bytes += skb_len;
|
||||
card->stats.rx_packets++;
|
||||
if (skb->protocol == htons(ETH_P_802_2))
|
||||
*((__u32 *)skb->cb) = ++card->pkt_seq;
|
||||
netif_rx(skb);
|
||||
}
|
||||
|
@ -1313,8 +1313,6 @@ static int netiucv_tx(struct sk_buff *skb, struct net_device *dev)
|
||||
* and throw away packet.
|
||||
*/
|
||||
if (fsm_getstate(privptr->fsm) != DEV_STATE_RUNNING) {
|
||||
if (!in_atomic())
|
||||
fsm_event(privptr->fsm, DEV_EVENT_START, dev);
|
||||
dev_kfree_skb(skb);
|
||||
privptr->stats.tx_dropped++;
|
||||
privptr->stats.tx_errors++;
|
||||
@ -2147,6 +2145,7 @@ static int __init netiucv_init(void)
|
||||
if (rc)
|
||||
goto out_dbf;
|
||||
IUCV_DBF_TEXT(trace, 3, __func__);
|
||||
netiucv_driver.groups = netiucv_drv_attr_groups;
|
||||
rc = driver_register(&netiucv_driver);
|
||||
if (rc) {
|
||||
PRINT_ERR("NETIUCV: failed to register driver.\n");
|
||||
|
@ -72,22 +72,7 @@ struct qeth_dbf_info {
|
||||
debug_sprintf_event(qeth_dbf[QETH_DBF_MSG].id, level, text)
|
||||
|
||||
#define QETH_DBF_TEXT_(name, level, text...) \
|
||||
do { \
|
||||
if (qeth_dbf_passes(qeth_dbf[QETH_DBF_##name].id, level)) { \
|
||||
char *dbf_txt_buf = \
|
||||
get_cpu_var(QETH_DBF_TXT_BUF); \
|
||||
sprintf(dbf_txt_buf, text); \
|
||||
debug_text_event(qeth_dbf[QETH_DBF_##name].id, \
|
||||
level, dbf_txt_buf); \
|
||||
put_cpu_var(QETH_DBF_TXT_BUF); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/* Allow to sort out low debug levels early to avoid wasted sprints */
|
||||
static inline int qeth_dbf_passes(debug_info_t *dbf_grp, int level)
|
||||
{
|
||||
return (level <= dbf_grp->level);
|
||||
}
|
||||
qeth_dbf_longtext(QETH_DBF_##name, level, text)
|
||||
|
||||
/**
|
||||
* some more debug stuff
|
||||
@ -773,27 +758,6 @@ static inline int qeth_get_micros(void)
|
||||
return (int) (get_clock() >> 12);
|
||||
}
|
||||
|
||||
static inline void *qeth_push_skb(struct qeth_card *card, struct sk_buff *skb,
|
||||
int size)
|
||||
{
|
||||
void *hdr;
|
||||
|
||||
hdr = (void *) skb_push(skb, size);
|
||||
/*
|
||||
* sanity check, the Linux memory allocation scheme should
|
||||
* never present us cases like this one (the qdio header size plus
|
||||
* the first 40 bytes of the paket cross a 4k boundary)
|
||||
*/
|
||||
if ((((unsigned long) hdr) & (~(PAGE_SIZE - 1))) !=
|
||||
(((unsigned long) hdr + size +
|
||||
QETH_IP_HEADER_SIZE) & (~(PAGE_SIZE - 1)))) {
|
||||
PRINT_ERR("Misaligned packet on interface %s. Discarded.",
|
||||
QETH_CARD_IFNAME(card));
|
||||
return NULL;
|
||||
}
|
||||
return hdr;
|
||||
}
|
||||
|
||||
static inline int qeth_get_ip_version(struct sk_buff *skb)
|
||||
{
|
||||
switch (skb->protocol) {
|
||||
@ -806,6 +770,12 @@ static inline int qeth_get_ip_version(struct sk_buff *skb)
|
||||
}
|
||||
}
|
||||
|
||||
static inline void qeth_put_buffer_pool_entry(struct qeth_card *card,
|
||||
struct qeth_buffer_pool_entry *entry)
|
||||
{
|
||||
list_add_tail(&entry->list, &card->qdio.in_buf_pool.entry_list);
|
||||
}
|
||||
|
||||
struct qeth_eddp_context;
|
||||
extern struct ccwgroup_driver qeth_l2_ccwgroup_driver;
|
||||
extern struct ccwgroup_driver qeth_l3_ccwgroup_driver;
|
||||
@ -843,8 +813,6 @@ struct qeth_cmd_buffer *qeth_get_ipacmd_buffer(struct qeth_card *,
|
||||
int qeth_query_setadapterparms(struct qeth_card *);
|
||||
int qeth_check_qdio_errors(struct qdio_buffer *, unsigned int,
|
||||
unsigned int, const char *);
|
||||
void qeth_put_buffer_pool_entry(struct qeth_card *,
|
||||
struct qeth_buffer_pool_entry *);
|
||||
void qeth_queue_input_buffer(struct qeth_card *, int);
|
||||
struct sk_buff *qeth_core_get_next_skb(struct qeth_card *,
|
||||
struct qdio_buffer *, struct qdio_buffer_element **, int *,
|
||||
@ -880,8 +848,6 @@ int qeth_send_control_data(struct qeth_card *, int, struct qeth_cmd_buffer *,
|
||||
void *reply_param);
|
||||
int qeth_get_cast_type(struct qeth_card *, struct sk_buff *);
|
||||
int qeth_get_priority_queue(struct qeth_card *, struct sk_buff *, int, int);
|
||||
struct sk_buff *qeth_prepare_skb(struct qeth_card *, struct sk_buff *,
|
||||
struct qeth_hdr **);
|
||||
int qeth_get_elements_no(struct qeth_card *, void *, struct sk_buff *, int);
|
||||
int qeth_do_send_packet_fast(struct qeth_card *, struct qeth_qdio_out_q *,
|
||||
struct sk_buff *, struct qeth_hdr *, int,
|
||||
@ -894,6 +860,8 @@ void qeth_core_get_ethtool_stats(struct net_device *,
|
||||
struct ethtool_stats *, u64 *);
|
||||
void qeth_core_get_strings(struct net_device *, u32, u8 *);
|
||||
void qeth_core_get_drvinfo(struct net_device *, struct ethtool_drvinfo *);
|
||||
void qeth_dbf_longtext(enum qeth_dbf_names dbf_nix, int level, char *text, ...);
|
||||
int qeth_core_ethtool_get_settings(struct net_device *, struct ethtool_cmd *);
|
||||
|
||||
/* exports for OSN */
|
||||
int qeth_osn_assist(struct net_device *, void *, int);
|
||||
|
@ -26,9 +26,6 @@
|
||||
#include "qeth_core.h"
|
||||
#include "qeth_core_offl.h"
|
||||
|
||||
static DEFINE_PER_CPU(char[256], qeth_core_dbf_txt_buf);
|
||||
#define QETH_DBF_TXT_BUF qeth_core_dbf_txt_buf
|
||||
|
||||
struct qeth_dbf_info qeth_dbf[QETH_DBF_INFOS] = {
|
||||
/* define dbf - Name, Pages, Areas, Maxlen, Level, View, Handle */
|
||||
/* N P A M L V H */
|
||||
@ -2255,14 +2252,6 @@ void qeth_print_status_message(struct qeth_card *card)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(qeth_print_status_message);
|
||||
|
||||
void qeth_put_buffer_pool_entry(struct qeth_card *card,
|
||||
struct qeth_buffer_pool_entry *entry)
|
||||
{
|
||||
QETH_DBF_TEXT(TRACE, 6, "ptbfplen");
|
||||
list_add_tail(&entry->list, &card->qdio.in_buf_pool.entry_list);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(qeth_put_buffer_pool_entry);
|
||||
|
||||
static void qeth_initialize_working_pool_list(struct qeth_card *card)
|
||||
{
|
||||
struct qeth_buffer_pool_entry *entry;
|
||||
@ -2603,7 +2592,6 @@ void qeth_queue_input_buffer(struct qeth_card *card, int index)
|
||||
int rc;
|
||||
int newcount = 0;
|
||||
|
||||
QETH_DBF_TEXT(TRACE, 6, "queinbuf");
|
||||
count = (index < queue->next_buf_to_init)?
|
||||
card->qdio.in_buf_pool.buf_count -
|
||||
(queue->next_buf_to_init - index) :
|
||||
@ -2792,8 +2780,6 @@ static void qeth_flush_buffers(struct qeth_qdio_out_q *queue, int under_int,
|
||||
int i;
|
||||
unsigned int qdio_flags;
|
||||
|
||||
QETH_DBF_TEXT(TRACE, 6, "flushbuf");
|
||||
|
||||
for (i = index; i < index + count; ++i) {
|
||||
buf = &queue->bufs[i % QDIO_MAX_BUFFERS_PER_Q];
|
||||
buf->buffer->element[buf->next_element_to_fill - 1].flags |=
|
||||
@ -3037,49 +3023,6 @@ int qeth_get_priority_queue(struct qeth_card *card, struct sk_buff *skb,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(qeth_get_priority_queue);
|
||||
|
||||
static void __qeth_free_new_skb(struct sk_buff *orig_skb,
|
||||
struct sk_buff *new_skb)
|
||||
{
|
||||
if (orig_skb != new_skb)
|
||||
dev_kfree_skb_any(new_skb);
|
||||
}
|
||||
|
||||
static inline struct sk_buff *qeth_realloc_headroom(struct qeth_card *card,
|
||||
struct sk_buff *skb, int size)
|
||||
{
|
||||
struct sk_buff *new_skb = skb;
|
||||
|
||||
if (skb_headroom(skb) >= size)
|
||||
return skb;
|
||||
new_skb = skb_realloc_headroom(skb, size);
|
||||
if (!new_skb)
|
||||
PRINT_ERR("Could not realloc headroom for qeth_hdr "
|
||||
"on interface %s", QETH_CARD_IFNAME(card));
|
||||
return new_skb;
|
||||
}
|
||||
|
||||
struct sk_buff *qeth_prepare_skb(struct qeth_card *card, struct sk_buff *skb,
|
||||
struct qeth_hdr **hdr)
|
||||
{
|
||||
struct sk_buff *new_skb;
|
||||
|
||||
QETH_DBF_TEXT(TRACE, 6, "prepskb");
|
||||
|
||||
new_skb = qeth_realloc_headroom(card, skb,
|
||||
sizeof(struct qeth_hdr));
|
||||
if (!new_skb)
|
||||
return NULL;
|
||||
|
||||
*hdr = ((struct qeth_hdr *)qeth_push_skb(card, new_skb,
|
||||
sizeof(struct qeth_hdr)));
|
||||
if (*hdr == NULL) {
|
||||
__qeth_free_new_skb(skb, new_skb);
|
||||
return NULL;
|
||||
}
|
||||
return new_skb;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(qeth_prepare_skb);
|
||||
|
||||
int qeth_get_elements_no(struct qeth_card *card, void *hdr,
|
||||
struct sk_buff *skb, int elems)
|
||||
{
|
||||
@ -3100,8 +3043,8 @@ int qeth_get_elements_no(struct qeth_card *card, void *hdr,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(qeth_get_elements_no);
|
||||
|
||||
static void __qeth_fill_buffer(struct sk_buff *skb, struct qdio_buffer *buffer,
|
||||
int is_tso, int *next_element_to_fill)
|
||||
static inline void __qeth_fill_buffer(struct sk_buff *skb,
|
||||
struct qdio_buffer *buffer, int is_tso, int *next_element_to_fill)
|
||||
{
|
||||
int length = skb->len;
|
||||
int length_here;
|
||||
@ -3143,15 +3086,13 @@ static void __qeth_fill_buffer(struct sk_buff *skb, struct qdio_buffer *buffer,
|
||||
*next_element_to_fill = element;
|
||||
}
|
||||
|
||||
static int qeth_fill_buffer(struct qeth_qdio_out_q *queue,
|
||||
static inline int qeth_fill_buffer(struct qeth_qdio_out_q *queue,
|
||||
struct qeth_qdio_out_buffer *buf, struct sk_buff *skb)
|
||||
{
|
||||
struct qdio_buffer *buffer;
|
||||
struct qeth_hdr_tso *hdr;
|
||||
int flush_cnt = 0, hdr_len, large_send = 0;
|
||||
|
||||
QETH_DBF_TEXT(TRACE, 6, "qdfillbf");
|
||||
|
||||
buffer = buf->buffer;
|
||||
atomic_inc(&skb->users);
|
||||
skb_queue_tail(&buf->skb_list, skb);
|
||||
@ -3210,8 +3151,6 @@ int qeth_do_send_packet_fast(struct qeth_card *card,
|
||||
int flush_cnt = 0;
|
||||
int index;
|
||||
|
||||
QETH_DBF_TEXT(TRACE, 6, "dosndpfa");
|
||||
|
||||
/* spin until we get the queue ... */
|
||||
while (atomic_cmpxchg(&queue->state, QETH_OUT_Q_UNLOCKED,
|
||||
QETH_OUT_Q_LOCKED) != QETH_OUT_Q_UNLOCKED);
|
||||
@ -3263,8 +3202,6 @@ int qeth_do_send_packet(struct qeth_card *card, struct qeth_qdio_out_q *queue,
|
||||
int tmp;
|
||||
int rc = 0;
|
||||
|
||||
QETH_DBF_TEXT(TRACE, 6, "dosndpkt");
|
||||
|
||||
/* spin until we get the queue ... */
|
||||
while (atomic_cmpxchg(&queue->state, QETH_OUT_Q_UNLOCKED,
|
||||
QETH_OUT_Q_LOCKED) != QETH_OUT_Q_UNLOCKED);
|
||||
@ -3827,27 +3764,8 @@ static struct ccw_driver qeth_ccw_driver = {
|
||||
static int qeth_core_driver_group(const char *buf, struct device *root_dev,
|
||||
unsigned long driver_id)
|
||||
{
|
||||
const char *start, *end;
|
||||
char bus_ids[3][BUS_ID_SIZE], *argv[3];
|
||||
int i;
|
||||
|
||||
start = buf;
|
||||
for (i = 0; i < 3; i++) {
|
||||
static const char delim[] = { ',', ',', '\n' };
|
||||
int len;
|
||||
|
||||
end = strchr(start, delim[i]);
|
||||
if (!end)
|
||||
return -EINVAL;
|
||||
len = min_t(ptrdiff_t, BUS_ID_SIZE, end - start);
|
||||
strncpy(bus_ids[i], start, len);
|
||||
bus_ids[i][len] = '\0';
|
||||
start = end + 1;
|
||||
argv[i] = bus_ids[i];
|
||||
}
|
||||
|
||||
return (ccwgroup_create(root_dev, driver_id,
|
||||
&qeth_ccw_driver, 3, argv));
|
||||
return ccwgroup_create_from_string(root_dev, driver_id,
|
||||
&qeth_ccw_driver, 3, buf);
|
||||
}
|
||||
|
||||
int qeth_core_hardsetup_card(struct qeth_card *card)
|
||||
@ -3885,8 +3803,9 @@ retry:
|
||||
QETH_DBF_TEXT_(SETUP, 2, "2err%d", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
mpno = QETH_MAX_PORTNO;
|
||||
mpno = qdio_get_ssqd_pct(CARD_DDEV(card));
|
||||
if (mpno)
|
||||
mpno = min(mpno - 1, QETH_MAX_PORTNO);
|
||||
if (card->info.portno > mpno) {
|
||||
PRINT_ERR("Device %s does not offer port number %d \n.",
|
||||
CARD_BUS_ID(card), card->info.portno);
|
||||
@ -3980,7 +3899,6 @@ struct sk_buff *qeth_core_get_next_skb(struct qeth_card *card,
|
||||
int use_rx_sg = 0;
|
||||
int frag = 0;
|
||||
|
||||
QETH_DBF_TEXT(TRACE, 6, "nextskb");
|
||||
/* qeth_hdr must not cross element boundaries */
|
||||
if (element->length < offset + sizeof(struct qeth_hdr)) {
|
||||
if (qeth_is_last_sbale(element))
|
||||
@ -4086,6 +4004,18 @@ static void qeth_unregister_dbf_views(void)
|
||||
}
|
||||
}
|
||||
|
||||
void qeth_dbf_longtext(enum qeth_dbf_names dbf_nix, int level, char *text, ...)
|
||||
{
|
||||
char dbf_txt_buf[32];
|
||||
|
||||
if (level > (qeth_dbf[dbf_nix].id)->level)
|
||||
return;
|
||||
snprintf(dbf_txt_buf, sizeof(dbf_txt_buf), text);
|
||||
debug_text_event(qeth_dbf[dbf_nix].id, level, dbf_txt_buf);
|
||||
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(qeth_dbf_longtext);
|
||||
|
||||
static int qeth_register_dbf_views(void)
|
||||
{
|
||||
int ret;
|
||||
@ -4433,6 +4363,96 @@ void qeth_core_get_drvinfo(struct net_device *dev,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(qeth_core_get_drvinfo);
|
||||
|
||||
int qeth_core_ethtool_get_settings(struct net_device *netdev,
|
||||
struct ethtool_cmd *ecmd)
|
||||
{
|
||||
struct qeth_card *card = netdev_priv(netdev);
|
||||
enum qeth_link_types link_type;
|
||||
|
||||
if ((card->info.type == QETH_CARD_TYPE_IQD) || (card->info.guestlan))
|
||||
link_type = QETH_LINK_TYPE_10GBIT_ETH;
|
||||
else
|
||||
link_type = card->info.link_type;
|
||||
|
||||
ecmd->transceiver = XCVR_INTERNAL;
|
||||
ecmd->supported = SUPPORTED_Autoneg;
|
||||
ecmd->advertising = ADVERTISED_Autoneg;
|
||||
ecmd->duplex = DUPLEX_FULL;
|
||||
ecmd->autoneg = AUTONEG_ENABLE;
|
||||
|
||||
switch (link_type) {
|
||||
case QETH_LINK_TYPE_FAST_ETH:
|
||||
case QETH_LINK_TYPE_LANE_ETH100:
|
||||
ecmd->supported |= SUPPORTED_10baseT_Half |
|
||||
SUPPORTED_10baseT_Full |
|
||||
SUPPORTED_100baseT_Half |
|
||||
SUPPORTED_100baseT_Full |
|
||||
SUPPORTED_TP;
|
||||
ecmd->advertising |= ADVERTISED_10baseT_Half |
|
||||
ADVERTISED_10baseT_Full |
|
||||
ADVERTISED_100baseT_Half |
|
||||
ADVERTISED_100baseT_Full |
|
||||
ADVERTISED_TP;
|
||||
ecmd->speed = SPEED_100;
|
||||
ecmd->port = PORT_TP;
|
||||
break;
|
||||
|
||||
case QETH_LINK_TYPE_GBIT_ETH:
|
||||
case QETH_LINK_TYPE_LANE_ETH1000:
|
||||
ecmd->supported |= SUPPORTED_10baseT_Half |
|
||||
SUPPORTED_10baseT_Full |
|
||||
SUPPORTED_100baseT_Half |
|
||||
SUPPORTED_100baseT_Full |
|
||||
SUPPORTED_1000baseT_Half |
|
||||
SUPPORTED_1000baseT_Full |
|
||||
SUPPORTED_FIBRE;
|
||||
ecmd->advertising |= ADVERTISED_10baseT_Half |
|
||||
ADVERTISED_10baseT_Full |
|
||||
ADVERTISED_100baseT_Half |
|
||||
ADVERTISED_100baseT_Full |
|
||||
ADVERTISED_1000baseT_Half |
|
||||
ADVERTISED_1000baseT_Full |
|
||||
ADVERTISED_FIBRE;
|
||||
ecmd->speed = SPEED_1000;
|
||||
ecmd->port = PORT_FIBRE;
|
||||
break;
|
||||
|
||||
case QETH_LINK_TYPE_10GBIT_ETH:
|
||||
ecmd->supported |= SUPPORTED_10baseT_Half |
|
||||
SUPPORTED_10baseT_Full |
|
||||
SUPPORTED_100baseT_Half |
|
||||
SUPPORTED_100baseT_Full |
|
||||
SUPPORTED_1000baseT_Half |
|
||||
SUPPORTED_1000baseT_Full |
|
||||
SUPPORTED_10000baseT_Full |
|
||||
SUPPORTED_FIBRE;
|
||||
ecmd->advertising |= ADVERTISED_10baseT_Half |
|
||||
ADVERTISED_10baseT_Full |
|
||||
ADVERTISED_100baseT_Half |
|
||||
ADVERTISED_100baseT_Full |
|
||||
ADVERTISED_1000baseT_Half |
|
||||
ADVERTISED_1000baseT_Full |
|
||||
ADVERTISED_10000baseT_Full |
|
||||
ADVERTISED_FIBRE;
|
||||
ecmd->speed = SPEED_10000;
|
||||
ecmd->port = PORT_FIBRE;
|
||||
break;
|
||||
|
||||
default:
|
||||
ecmd->supported |= SUPPORTED_10baseT_Half |
|
||||
SUPPORTED_10baseT_Full |
|
||||
SUPPORTED_TP;
|
||||
ecmd->advertising |= ADVERTISED_10baseT_Half |
|
||||
ADVERTISED_10baseT_Full |
|
||||
ADVERTISED_TP;
|
||||
ecmd->speed = SPEED_10;
|
||||
ecmd->port = PORT_TP;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(qeth_core_ethtool_get_settings);
|
||||
|
||||
static int __init qeth_core_init(void)
|
||||
{
|
||||
int rc;
|
||||
|
@ -22,9 +22,6 @@
|
||||
#include "qeth_core.h"
|
||||
#include "qeth_core_offl.h"
|
||||
|
||||
#define QETH_DBF_TXT_BUF qeth_l2_dbf_txt_buf
|
||||
static DEFINE_PER_CPU(char[256], qeth_l2_dbf_txt_buf);
|
||||
|
||||
static int qeth_l2_set_offline(struct ccwgroup_device *);
|
||||
static int qeth_l2_stop(struct net_device *);
|
||||
static int qeth_l2_send_delmac(struct qeth_card *, __u8 *);
|
||||
@ -635,8 +632,6 @@ static int qeth_l2_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
|
||||
enum qeth_large_send_types large_send = QETH_LARGE_SEND_NO;
|
||||
struct qeth_eddp_context *ctx = NULL;
|
||||
|
||||
QETH_DBF_TEXT(TRACE, 6, "l2xmit");
|
||||
|
||||
if ((card->state != CARD_STATE_UP) || !card->lan_online) {
|
||||
card->stats.tx_carrier_errors++;
|
||||
goto tx_drop;
|
||||
@ -658,9 +653,12 @@ static int qeth_l2_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
|
||||
if (card->info.type == QETH_CARD_TYPE_OSN)
|
||||
hdr = (struct qeth_hdr *)skb->data;
|
||||
else {
|
||||
new_skb = qeth_prepare_skb(card, skb, &hdr);
|
||||
/* create a clone with writeable headroom */
|
||||
new_skb = skb_realloc_headroom(skb, sizeof(struct qeth_hdr));
|
||||
if (!new_skb)
|
||||
goto tx_drop;
|
||||
hdr = (struct qeth_hdr *)skb_push(new_skb,
|
||||
sizeof(struct qeth_hdr));
|
||||
qeth_l2_fill_header(card, hdr, new_skb, ipv, cast_type);
|
||||
}
|
||||
|
||||
@ -747,7 +745,6 @@ static void qeth_l2_qdio_input_handler(struct ccw_device *ccwdev,
|
||||
int index;
|
||||
int i;
|
||||
|
||||
QETH_DBF_TEXT(TRACE, 6, "qdinput");
|
||||
card = (struct qeth_card *) card_ptr;
|
||||
net_dev = card->dev;
|
||||
if (card->options.performance_stats) {
|
||||
@ -852,6 +849,22 @@ static void qeth_l2_remove_device(struct ccwgroup_device *cgdev)
|
||||
return;
|
||||
}
|
||||
|
||||
static int qeth_l2_ethtool_set_tso(struct net_device *dev, u32 data)
|
||||
{
|
||||
struct qeth_card *card = netdev_priv(dev);
|
||||
|
||||
if (data) {
|
||||
if (card->options.large_send == QETH_LARGE_SEND_NO) {
|
||||
card->options.large_send = QETH_LARGE_SEND_EDDP;
|
||||
dev->features |= NETIF_F_TSO;
|
||||
}
|
||||
} else {
|
||||
dev->features &= ~NETIF_F_TSO;
|
||||
card->options.large_send = QETH_LARGE_SEND_NO;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct ethtool_ops qeth_l2_ethtool_ops = {
|
||||
.get_link = ethtool_op_get_link,
|
||||
.get_tx_csum = ethtool_op_get_tx_csum,
|
||||
@ -859,11 +872,12 @@ static struct ethtool_ops qeth_l2_ethtool_ops = {
|
||||
.get_sg = ethtool_op_get_sg,
|
||||
.set_sg = ethtool_op_set_sg,
|
||||
.get_tso = ethtool_op_get_tso,
|
||||
.set_tso = ethtool_op_set_tso,
|
||||
.set_tso = qeth_l2_ethtool_set_tso,
|
||||
.get_strings = qeth_core_get_strings,
|
||||
.get_ethtool_stats = qeth_core_get_ethtool_stats,
|
||||
.get_stats_count = qeth_core_get_stats_count,
|
||||
.get_drvinfo = qeth_core_get_drvinfo,
|
||||
.get_settings = qeth_core_ethtool_get_settings,
|
||||
};
|
||||
|
||||
static struct ethtool_ops qeth_l2_osn_ops = {
|
||||
|
@ -13,9 +13,6 @@
|
||||
|
||||
#include "qeth_core.h"
|
||||
|
||||
#define QETH_DBF_TXT_BUF qeth_l3_dbf_txt_buf
|
||||
DECLARE_PER_CPU(char[256], qeth_l3_dbf_txt_buf);
|
||||
|
||||
struct qeth_ipaddr {
|
||||
struct list_head entry;
|
||||
enum qeth_ip_types type;
|
||||
|
@ -28,8 +28,6 @@
|
||||
#include "qeth_l3.h"
|
||||
#include "qeth_core_offl.h"
|
||||
|
||||
DEFINE_PER_CPU(char[256], qeth_l3_dbf_txt_buf);
|
||||
|
||||
static int qeth_l3_set_offline(struct ccwgroup_device *);
|
||||
static int qeth_l3_recover(void *);
|
||||
static int qeth_l3_stop(struct net_device *);
|
||||
@ -2093,6 +2091,11 @@ static int qeth_l3_stop_card(struct qeth_card *card, int recovery_mode)
|
||||
(card->state == CARD_STATE_UP)) {
|
||||
if (recovery_mode)
|
||||
qeth_l3_stop(card->dev);
|
||||
else {
|
||||
rtnl_lock();
|
||||
dev_close(card->dev);
|
||||
rtnl_unlock();
|
||||
}
|
||||
if (!card->use_hard_stop) {
|
||||
rc = qeth_send_stoplan(card);
|
||||
if (rc)
|
||||
@ -2559,8 +2562,6 @@ static int qeth_l3_do_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
|
||||
static void qeth_l3_fill_header(struct qeth_card *card, struct qeth_hdr *hdr,
|
||||
struct sk_buff *skb, int ipv, int cast_type)
|
||||
{
|
||||
QETH_DBF_TEXT(TRACE, 6, "fillhdr");
|
||||
|
||||
memset(hdr, 0, sizeof(struct qeth_hdr));
|
||||
hdr->hdr.l3.id = QETH_HEADER_TYPE_LAYER3;
|
||||
hdr->hdr.l3.ext_flags = 0;
|
||||
@ -2570,9 +2571,10 @@ static void qeth_l3_fill_header(struct qeth_card *card, struct qeth_hdr *hdr,
|
||||
* v6 uses passthrough, v4 sets the tag in the QDIO header.
|
||||
*/
|
||||
if (card->vlangrp && vlan_tx_tag_present(skb)) {
|
||||
hdr->hdr.l3.ext_flags = (ipv == 4) ?
|
||||
QETH_HDR_EXT_VLAN_FRAME :
|
||||
QETH_HDR_EXT_INCLUDE_VLAN_TAG;
|
||||
if ((ipv == 4) || (card->info.type == QETH_CARD_TYPE_IQD))
|
||||
hdr->hdr.l3.ext_flags = QETH_HDR_EXT_VLAN_FRAME;
|
||||
else
|
||||
hdr->hdr.l3.ext_flags = QETH_HDR_EXT_INCLUDE_VLAN_TAG;
|
||||
hdr->hdr.l3.vlan_id = vlan_tx_tag_get(skb);
|
||||
}
|
||||
|
||||
@ -2638,8 +2640,6 @@ static int qeth_l3_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
|
||||
enum qeth_large_send_types large_send = QETH_LARGE_SEND_NO;
|
||||
struct qeth_eddp_context *ctx = NULL;
|
||||
|
||||
QETH_DBF_TEXT(TRACE, 6, "l3xmit");
|
||||
|
||||
if ((card->info.type == QETH_CARD_TYPE_IQD) &&
|
||||
(skb->protocol != htons(ETH_P_IPV6)) &&
|
||||
(skb->protocol != htons(ETH_P_IP)))
|
||||
@ -2890,6 +2890,7 @@ static struct ethtool_ops qeth_l3_ethtool_ops = {
|
||||
.get_ethtool_stats = qeth_core_get_ethtool_stats,
|
||||
.get_stats_count = qeth_core_get_stats_count,
|
||||
.get_drvinfo = qeth_core_get_drvinfo,
|
||||
.get_settings = qeth_core_ethtool_get_settings,
|
||||
};
|
||||
|
||||
/*
|
||||
@ -2982,7 +2983,6 @@ static void qeth_l3_qdio_input_handler(struct ccw_device *ccwdev,
|
||||
int index;
|
||||
int i;
|
||||
|
||||
QETH_DBF_TEXT(TRACE, 6, "qdinput");
|
||||
card = (struct qeth_card *) card_ptr;
|
||||
net_dev = card->dev;
|
||||
if (card->options.performance_stats) {
|
||||
@ -3140,8 +3140,14 @@ static int __qeth_l3_set_online(struct ccwgroup_device *gdev, int recovery_mode)
|
||||
netif_carrier_on(card->dev);
|
||||
|
||||
qeth_set_allowed_threads(card, 0xffffffff, 0);
|
||||
if ((recover_flag == CARD_STATE_RECOVER) && recovery_mode) {
|
||||
if (recover_flag == CARD_STATE_RECOVER) {
|
||||
if (recovery_mode)
|
||||
qeth_l3_open(card->dev);
|
||||
else {
|
||||
rtnl_lock();
|
||||
dev_open(card->dev);
|
||||
rtnl_unlock();
|
||||
}
|
||||
qeth_l3_set_multicast_list(card->dev);
|
||||
}
|
||||
/* let user_space know that device is online */
|
||||
|
@ -57,10 +57,9 @@ struct ccwgroup_driver {
|
||||
|
||||
extern int ccwgroup_driver_register (struct ccwgroup_driver *cdriver);
|
||||
extern void ccwgroup_driver_unregister (struct ccwgroup_driver *cdriver);
|
||||
extern int ccwgroup_create (struct device *root,
|
||||
unsigned int creator_id,
|
||||
struct ccw_driver *gdrv,
|
||||
int argc, char *argv[]);
|
||||
int ccwgroup_create_from_string(struct device *root, unsigned int creator_id,
|
||||
struct ccw_driver *cdrv, int num_devices,
|
||||
const char *buf);
|
||||
|
||||
extern int ccwgroup_probe_ccwdev(struct ccw_device *cdev);
|
||||
extern void ccwgroup_remove_ccwdev(struct ccw_device *cdev);
|
||||
|
@ -127,6 +127,7 @@ extern int do_QDIO(struct ccw_device*, unsigned int flags,
|
||||
unsigned int qidx,unsigned int count,
|
||||
struct qdio_buffer *buffers);
|
||||
|
||||
extern int qdio_get_ssqd_pct(struct ccw_device*);
|
||||
extern int qdio_synchronize(struct ccw_device*, unsigned int flags,
|
||||
unsigned int queue_number);
|
||||
|
||||
|
@ -42,5 +42,8 @@ extern int cmsghdr_from_user_compat_to_kern(struct msghdr *, struct sock *, unsi
|
||||
|
||||
extern int compat_mc_setsockopt(struct sock *, int, int, char __user *, int,
|
||||
int (*)(struct sock *, int, int, char __user *, int));
|
||||
extern int compat_mc_getsockopt(struct sock *, int, int, char __user *,
|
||||
int __user *, int (*)(struct sock *, int, int, char __user *,
|
||||
int __user *));
|
||||
|
||||
#endif /* NET_COMPAT_H */
|
||||
|
@ -405,7 +405,8 @@ struct sk_buff;
|
||||
struct ip_vs_protocol {
|
||||
struct ip_vs_protocol *next;
|
||||
char *name;
|
||||
__u16 protocol;
|
||||
u16 protocol;
|
||||
u16 num_states;
|
||||
int dont_defrag;
|
||||
atomic_t appcnt; /* counter of proto app incs */
|
||||
int *timeout_table; /* protocol timeout table */
|
||||
|
@ -107,16 +107,6 @@ struct nf_conntrack_tuple_mask
|
||||
} src;
|
||||
};
|
||||
|
||||
/* This is optimized opposed to a memset of the whole structure. Everything we
|
||||
* really care about is the source/destination unions */
|
||||
#define NF_CT_TUPLE_U_BLANK(tuple) \
|
||||
do { \
|
||||
(tuple)->src.u.all = 0; \
|
||||
(tuple)->dst.u.all = 0; \
|
||||
memset(&(tuple)->src.u3, 0, sizeof((tuple)->src.u3)); \
|
||||
memset(&(tuple)->dst.u3, 0, sizeof((tuple)->dst.u3)); \
|
||||
} while (0)
|
||||
|
||||
#ifdef __KERNEL__
|
||||
|
||||
static inline void nf_ct_dump_tuple_ip(const struct nf_conntrack_tuple *t)
|
||||
|
@ -116,7 +116,7 @@ extern int __ip_route_output_key(struct net *, struct rtable **, const struct f
|
||||
extern int ip_route_output_key(struct net *, struct rtable **, struct flowi *flp);
|
||||
extern int ip_route_output_flow(struct net *, struct rtable **rp, struct flowi *flp, struct sock *sk, int flags);
|
||||
extern int ip_route_input(struct sk_buff*, __be32 dst, __be32 src, u8 tos, struct net_device *devin);
|
||||
extern unsigned short ip_rt_frag_needed(struct net *net, struct iphdr *iph, unsigned short new_mtu);
|
||||
extern unsigned short ip_rt_frag_needed(struct net *net, struct iphdr *iph, unsigned short new_mtu, struct net_device *dev);
|
||||
extern void ip_rt_send_redirect(struct sk_buff *skb);
|
||||
|
||||
extern unsigned inet_addr_type(struct net *net, __be32 addr);
|
||||
|
@ -76,7 +76,6 @@ static void __exit br_deinit(void)
|
||||
rcu_assign_pointer(br_stp_sap->rcv_func, NULL);
|
||||
|
||||
br_netlink_fini();
|
||||
br_netfilter_fini();
|
||||
unregister_netdevice_notifier(&br_device_notifier);
|
||||
brioctl_set(NULL);
|
||||
|
||||
@ -84,6 +83,7 @@ static void __exit br_deinit(void)
|
||||
|
||||
synchronize_net();
|
||||
|
||||
br_netfilter_fini();
|
||||
llc_sap_put(br_stp_sap);
|
||||
br_fdb_get_hook = NULL;
|
||||
br_fdb_put_hook = NULL;
|
||||
|
@ -411,9 +411,12 @@ err2:
|
||||
br_fdb_delete_by_port(br, p, 1);
|
||||
err1:
|
||||
kobject_del(&p->kobj);
|
||||
return err;
|
||||
goto put_back;
|
||||
err0:
|
||||
kobject_put(&p->kobj);
|
||||
|
||||
put_back:
|
||||
dev_put(dev);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
90
net/compat.c
90
net/compat.c
@ -548,6 +548,9 @@ struct compat_group_filter {
|
||||
__attribute__ ((aligned(4)));
|
||||
} __attribute__ ((packed));
|
||||
|
||||
#define __COMPAT_GF0_SIZE (sizeof(struct compat_group_filter) - \
|
||||
sizeof(struct __kernel_sockaddr_storage))
|
||||
|
||||
|
||||
int compat_mc_setsockopt(struct sock *sock, int level, int optname,
|
||||
char __user *optval, int optlen,
|
||||
@ -582,7 +585,7 @@ int compat_mc_setsockopt(struct sock *sock, int level, int optname,
|
||||
case MCAST_UNBLOCK_SOURCE:
|
||||
{
|
||||
struct compat_group_source_req __user *gsr32 = (void *)optval;
|
||||
struct group_source_req *kgsr = compat_alloc_user_space(
|
||||
struct group_source_req __user *kgsr = compat_alloc_user_space(
|
||||
sizeof(struct group_source_req));
|
||||
u32 interface;
|
||||
|
||||
@ -603,10 +606,10 @@ int compat_mc_setsockopt(struct sock *sock, int level, int optname,
|
||||
case MCAST_MSFILTER:
|
||||
{
|
||||
struct compat_group_filter __user *gf32 = (void *)optval;
|
||||
struct group_filter *kgf;
|
||||
struct group_filter __user *kgf;
|
||||
u32 interface, fmode, numsrc;
|
||||
|
||||
if (!access_ok(VERIFY_READ, gf32, sizeof(*gf32)) ||
|
||||
if (!access_ok(VERIFY_READ, gf32, __COMPAT_GF0_SIZE) ||
|
||||
__get_user(interface, &gf32->gf_interface) ||
|
||||
__get_user(fmode, &gf32->gf_fmode) ||
|
||||
__get_user(numsrc, &gf32->gf_numsrc))
|
||||
@ -622,7 +625,7 @@ int compat_mc_setsockopt(struct sock *sock, int level, int optname,
|
||||
__put_user(numsrc, &kgf->gf_numsrc) ||
|
||||
copy_in_user(&kgf->gf_group, &gf32->gf_group,
|
||||
sizeof(kgf->gf_group)) ||
|
||||
(numsrc && copy_in_user(&kgf->gf_slist, &gf32->gf_slist,
|
||||
(numsrc && copy_in_user(kgf->gf_slist, gf32->gf_slist,
|
||||
numsrc * sizeof(kgf->gf_slist[0]))))
|
||||
return -EFAULT;
|
||||
koptval = (char __user *)kgf;
|
||||
@ -637,6 +640,85 @@ int compat_mc_setsockopt(struct sock *sock, int level, int optname,
|
||||
|
||||
EXPORT_SYMBOL(compat_mc_setsockopt);
|
||||
|
||||
int compat_mc_getsockopt(struct sock *sock, int level, int optname,
|
||||
char __user *optval, int __user *optlen,
|
||||
int (*getsockopt)(struct sock *,int,int,char __user *,int __user *))
|
||||
{
|
||||
struct compat_group_filter __user *gf32 = (void *)optval;
|
||||
struct group_filter __user *kgf;
|
||||
int __user *koptlen;
|
||||
u32 interface, fmode, numsrc;
|
||||
int klen, ulen, err;
|
||||
|
||||
if (optname != MCAST_MSFILTER)
|
||||
return getsockopt(sock, level, optname, optval, optlen);
|
||||
|
||||
koptlen = compat_alloc_user_space(sizeof(*koptlen));
|
||||
if (!access_ok(VERIFY_READ, optlen, sizeof(*optlen)) ||
|
||||
__get_user(ulen, optlen))
|
||||
return -EFAULT;
|
||||
|
||||
/* adjust len for pad */
|
||||
klen = ulen + sizeof(*kgf) - sizeof(*gf32);
|
||||
|
||||
if (klen < GROUP_FILTER_SIZE(0))
|
||||
return -EINVAL;
|
||||
|
||||
if (!access_ok(VERIFY_WRITE, koptlen, sizeof(*koptlen)) ||
|
||||
__put_user(klen, koptlen))
|
||||
return -EFAULT;
|
||||
|
||||
/* have to allow space for previous compat_alloc_user_space, too */
|
||||
kgf = compat_alloc_user_space(klen+sizeof(*optlen));
|
||||
|
||||
if (!access_ok(VERIFY_READ, gf32, __COMPAT_GF0_SIZE) ||
|
||||
__get_user(interface, &gf32->gf_interface) ||
|
||||
__get_user(fmode, &gf32->gf_fmode) ||
|
||||
__get_user(numsrc, &gf32->gf_numsrc) ||
|
||||
__put_user(interface, &kgf->gf_interface) ||
|
||||
__put_user(fmode, &kgf->gf_fmode) ||
|
||||
__put_user(numsrc, &kgf->gf_numsrc) ||
|
||||
copy_in_user(&kgf->gf_group,&gf32->gf_group,sizeof(kgf->gf_group)))
|
||||
return -EFAULT;
|
||||
|
||||
err = getsockopt(sock, level, optname, (char __user *)kgf, koptlen);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (!access_ok(VERIFY_READ, koptlen, sizeof(*koptlen)) ||
|
||||
__get_user(klen, koptlen))
|
||||
return -EFAULT;
|
||||
|
||||
ulen = klen - (sizeof(*kgf)-sizeof(*gf32));
|
||||
|
||||
if (!access_ok(VERIFY_WRITE, optlen, sizeof(*optlen)) ||
|
||||
__put_user(ulen, optlen))
|
||||
return -EFAULT;
|
||||
|
||||
if (!access_ok(VERIFY_READ, kgf, klen) ||
|
||||
!access_ok(VERIFY_WRITE, gf32, ulen) ||
|
||||
__get_user(interface, &kgf->gf_interface) ||
|
||||
__get_user(fmode, &kgf->gf_fmode) ||
|
||||
__get_user(numsrc, &kgf->gf_numsrc) ||
|
||||
__put_user(interface, &gf32->gf_interface) ||
|
||||
__put_user(fmode, &gf32->gf_fmode) ||
|
||||
__put_user(numsrc, &gf32->gf_numsrc))
|
||||
return -EFAULT;
|
||||
if (numsrc) {
|
||||
int copylen;
|
||||
|
||||
klen -= GROUP_FILTER_SIZE(0);
|
||||
copylen = numsrc * sizeof(gf32->gf_slist[0]);
|
||||
if (copylen > klen)
|
||||
copylen = klen;
|
||||
if (copy_in_user(gf32->gf_slist, kgf->gf_slist, copylen))
|
||||
return -EFAULT;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(compat_mc_getsockopt);
|
||||
|
||||
|
||||
/* Argument list sizes for compat_sys_socketcall */
|
||||
#define AL(x) ((x) * sizeof(u32))
|
||||
|
@ -691,7 +691,8 @@ static void icmp_unreach(struct sk_buff *skb)
|
||||
NIPQUAD(iph->daddr));
|
||||
} else {
|
||||
info = ip_rt_frag_needed(net, iph,
|
||||
ntohs(icmph->un.frag.mtu));
|
||||
ntohs(icmph->un.frag.mtu),
|
||||
skb->dev);
|
||||
if (!info)
|
||||
goto out;
|
||||
}
|
||||
|
@ -753,23 +753,15 @@ static inline int ip_ufo_append_data(struct sock *sk,
|
||||
skb->ip_summed = CHECKSUM_PARTIAL;
|
||||
skb->csum = 0;
|
||||
sk->sk_sndmsg_off = 0;
|
||||
}
|
||||
|
||||
err = skb_append_datato_frags(sk,skb, getfrag, from,
|
||||
(length - transhdrlen));
|
||||
if (!err) {
|
||||
/* specify the length of each IP datagram fragment */
|
||||
skb_shinfo(skb)->gso_size = mtu - fragheaderlen;
|
||||
skb_shinfo(skb)->gso_type = SKB_GSO_UDP;
|
||||
__skb_queue_tail(&sk->sk_write_queue, skb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
/* There is not enough support do UFO ,
|
||||
* so follow normal path
|
||||
*/
|
||||
kfree_skb(skb);
|
||||
return err;
|
||||
|
||||
return skb_append_datato_frags(sk, skb, getfrag, from,
|
||||
(length - transhdrlen));
|
||||
}
|
||||
|
||||
/*
|
||||
@ -863,9 +855,9 @@ int ip_append_data(struct sock *sk,
|
||||
csummode = CHECKSUM_PARTIAL;
|
||||
|
||||
inet->cork.length += length;
|
||||
if (((length > mtu) && (sk->sk_protocol == IPPROTO_UDP)) &&
|
||||
if (((length> mtu) || !skb_queue_empty(&sk->sk_write_queue)) &&
|
||||
(sk->sk_protocol == IPPROTO_UDP) &&
|
||||
(rt->u.dst.dev->features & NETIF_F_UFO)) {
|
||||
|
||||
err = ip_ufo_append_data(sk, getfrag, from, length, hh_len,
|
||||
fragheaderlen, transhdrlen, mtu,
|
||||
flags);
|
||||
|
@ -1186,7 +1186,14 @@ int ip_getsockopt(struct sock *sk, int level,
|
||||
int compat_ip_getsockopt(struct sock *sk, int level, int optname,
|
||||
char __user *optval, int __user *optlen)
|
||||
{
|
||||
int err = do_ip_getsockopt(sk, level, optname, optval, optlen);
|
||||
int err;
|
||||
|
||||
if (optname == MCAST_MSFILTER)
|
||||
return compat_mc_getsockopt(sk, level, optname, optval, optlen,
|
||||
ip_getsockopt);
|
||||
|
||||
err = do_ip_getsockopt(sk, level, optname, optval, optlen);
|
||||
|
||||
#ifdef CONFIG_NETFILTER
|
||||
/* we need to exclude all possible ENOPROTOOPTs except default case */
|
||||
if (err == -ENOPROTOOPT && optname != IP_PKTOPTIONS &&
|
||||
|
@ -412,12 +412,12 @@ static struct packet_type rarp_packet_type __initdata = {
|
||||
.func = ic_rarp_recv,
|
||||
};
|
||||
|
||||
static inline void ic_rarp_init(void)
|
||||
static inline void __init ic_rarp_init(void)
|
||||
{
|
||||
dev_add_pack(&rarp_packet_type);
|
||||
}
|
||||
|
||||
static inline void ic_rarp_cleanup(void)
|
||||
static inline void __init ic_rarp_cleanup(void)
|
||||
{
|
||||
dev_remove_pack(&rarp_packet_type);
|
||||
}
|
||||
@ -682,7 +682,7 @@ static void __init ic_bootp_init_ext(u8 *e)
|
||||
/*
|
||||
* Initialize the DHCP/BOOTP mechanism.
|
||||
*/
|
||||
static inline void ic_bootp_init(void)
|
||||
static inline void __init ic_bootp_init(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
@ -696,7 +696,7 @@ static inline void ic_bootp_init(void)
|
||||
/*
|
||||
* DHCP/BOOTP cleanup.
|
||||
*/
|
||||
static inline void ic_bootp_cleanup(void)
|
||||
static inline void __init ic_bootp_cleanup(void)
|
||||
{
|
||||
dev_remove_pack(&bootp_packet_type);
|
||||
}
|
||||
|
@ -148,7 +148,7 @@ const char * ip_vs_state_name(__u16 proto, int state)
|
||||
struct ip_vs_protocol *pp = ip_vs_proto_get(proto);
|
||||
|
||||
if (pp == NULL || pp->state_name == NULL)
|
||||
return "ERR!";
|
||||
return (IPPROTO_IP == proto) ? "NONE" : "ERR!";
|
||||
return pp->state_name(state);
|
||||
}
|
||||
|
||||
|
@ -160,6 +160,7 @@ static void ah_exit(struct ip_vs_protocol *pp)
|
||||
struct ip_vs_protocol ip_vs_protocol_ah = {
|
||||
.name = "AH",
|
||||
.protocol = IPPROTO_AH,
|
||||
.num_states = 1,
|
||||
.dont_defrag = 1,
|
||||
.init = ah_init,
|
||||
.exit = ah_exit,
|
||||
|
@ -159,6 +159,7 @@ static void esp_exit(struct ip_vs_protocol *pp)
|
||||
struct ip_vs_protocol ip_vs_protocol_esp = {
|
||||
.name = "ESP",
|
||||
.protocol = IPPROTO_ESP,
|
||||
.num_states = 1,
|
||||
.dont_defrag = 1,
|
||||
.init = esp_init,
|
||||
.exit = esp_exit,
|
||||
|
@ -594,6 +594,7 @@ static void ip_vs_tcp_exit(struct ip_vs_protocol *pp)
|
||||
struct ip_vs_protocol ip_vs_protocol_tcp = {
|
||||
.name = "TCP",
|
||||
.protocol = IPPROTO_TCP,
|
||||
.num_states = IP_VS_TCP_S_LAST,
|
||||
.dont_defrag = 0,
|
||||
.appcnt = ATOMIC_INIT(0),
|
||||
.init = ip_vs_tcp_init,
|
||||
|
@ -409,6 +409,7 @@ static void udp_exit(struct ip_vs_protocol *pp)
|
||||
struct ip_vs_protocol ip_vs_protocol_udp = {
|
||||
.name = "UDP",
|
||||
.protocol = IPPROTO_UDP,
|
||||
.num_states = IP_VS_UDP_S_LAST,
|
||||
.dont_defrag = 0,
|
||||
.init = udp_init,
|
||||
.exit = udp_exit,
|
||||
|
@ -288,11 +288,16 @@ static void ip_vs_process_message(const char *buffer, const size_t buflen)
|
||||
char *p;
|
||||
int i;
|
||||
|
||||
if (buflen < sizeof(struct ip_vs_sync_mesg)) {
|
||||
IP_VS_ERR_RL("sync message header too short\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Convert size back to host byte order */
|
||||
m->size = ntohs(m->size);
|
||||
|
||||
if (buflen != m->size) {
|
||||
IP_VS_ERR("bogus message\n");
|
||||
IP_VS_ERR_RL("bogus sync message size\n");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -307,9 +312,48 @@ static void ip_vs_process_message(const char *buffer, const size_t buflen)
|
||||
for (i=0; i<m->nr_conns; i++) {
|
||||
unsigned flags, state;
|
||||
|
||||
if (p + SIMPLE_CONN_SIZE > buffer+buflen) {
|
||||
IP_VS_ERR_RL("bogus conn in sync message\n");
|
||||
return;
|
||||
}
|
||||
s = (struct ip_vs_sync_conn *) p;
|
||||
flags = ntohs(s->flags) | IP_VS_CONN_F_SYNC;
|
||||
flags &= ~IP_VS_CONN_F_HASHED;
|
||||
if (flags & IP_VS_CONN_F_SEQ_MASK) {
|
||||
opt = (struct ip_vs_sync_conn_options *)&s[1];
|
||||
p += FULL_CONN_SIZE;
|
||||
if (p > buffer+buflen) {
|
||||
IP_VS_ERR_RL("bogus conn options in sync message\n");
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
opt = NULL;
|
||||
p += SIMPLE_CONN_SIZE;
|
||||
}
|
||||
|
||||
state = ntohs(s->state);
|
||||
if (!(flags & IP_VS_CONN_F_TEMPLATE)) {
|
||||
pp = ip_vs_proto_get(s->protocol);
|
||||
if (!pp) {
|
||||
IP_VS_ERR_RL("Unsupported protocol %u in sync msg\n",
|
||||
s->protocol);
|
||||
continue;
|
||||
}
|
||||
if (state >= pp->num_states) {
|
||||
IP_VS_DBG(2, "Invalid %s state %u in sync msg\n",
|
||||
pp->name, state);
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
/* protocol in templates is not used for state/timeout */
|
||||
pp = NULL;
|
||||
if (state > 0) {
|
||||
IP_VS_DBG(2, "Invalid template state %u in sync msg\n",
|
||||
state);
|
||||
state = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (!(flags & IP_VS_CONN_F_TEMPLATE))
|
||||
cp = ip_vs_conn_in_get(s->protocol,
|
||||
s->caddr, s->cport,
|
||||
@ -345,14 +389,9 @@ static void ip_vs_process_message(const char *buffer, const size_t buflen)
|
||||
IP_VS_ERR("ip_vs_conn_new failed\n");
|
||||
return;
|
||||
}
|
||||
cp->state = state;
|
||||
} else if (!cp->dest) {
|
||||
dest = ip_vs_try_bind_dest(cp);
|
||||
if (!dest) {
|
||||
/* it is an unbound entry created by
|
||||
* synchronization */
|
||||
cp->flags = flags | IP_VS_CONN_F_HASHED;
|
||||
} else
|
||||
if (dest)
|
||||
atomic_dec(&dest->refcnt);
|
||||
} else if ((cp->dest) && (cp->protocol == IPPROTO_TCP) &&
|
||||
(cp->state != state)) {
|
||||
@ -371,23 +410,22 @@ static void ip_vs_process_message(const char *buffer, const size_t buflen)
|
||||
}
|
||||
}
|
||||
|
||||
if (flags & IP_VS_CONN_F_SEQ_MASK) {
|
||||
opt = (struct ip_vs_sync_conn_options *)&s[1];
|
||||
if (opt)
|
||||
memcpy(&cp->in_seq, opt, sizeof(*opt));
|
||||
p += FULL_CONN_SIZE;
|
||||
} else
|
||||
p += SIMPLE_CONN_SIZE;
|
||||
|
||||
atomic_set(&cp->in_pkts, sysctl_ip_vs_sync_threshold[0]);
|
||||
cp->state = state;
|
||||
pp = ip_vs_proto_get(s->protocol);
|
||||
cp->timeout = pp->timeout_table[cp->state];
|
||||
cp->old_state = cp->state;
|
||||
/*
|
||||
* We can not recover the right timeout for templates
|
||||
* in all cases, we can not find the right fwmark
|
||||
* virtual service. If needed, we can do it for
|
||||
* non-fwmark persistent services.
|
||||
*/
|
||||
if (!(flags & IP_VS_CONN_F_TEMPLATE) && pp->timeout_table)
|
||||
cp->timeout = pp->timeout_table[state];
|
||||
else
|
||||
cp->timeout = (3*60*HZ);
|
||||
ip_vs_conn_put(cp);
|
||||
|
||||
if (p > buffer+buflen) {
|
||||
IP_VS_ERR("bogus message\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -296,9 +296,8 @@ ipq_mangle_ipv4(ipq_verdict_msg_t *v, struct nf_queue_entry *e)
|
||||
if (v->data_len > 0xFFFF)
|
||||
return -EINVAL;
|
||||
if (diff > skb_tailroom(e->skb)) {
|
||||
nskb = skb_copy_expand(e->skb, 0,
|
||||
diff - skb_tailroom(e->skb),
|
||||
GFP_ATOMIC);
|
||||
nskb = skb_copy_expand(e->skb, skb_headroom(e->skb),
|
||||
diff, GFP_ATOMIC);
|
||||
if (!nskb) {
|
||||
printk(KERN_WARNING "ip_queue: error "
|
||||
"in mangle, dropping packet\n");
|
||||
|
@ -303,7 +303,7 @@ getorigdst(struct sock *sk, int optval, void __user *user, int *len)
|
||||
const struct nf_conntrack_tuple_hash *h;
|
||||
struct nf_conntrack_tuple tuple;
|
||||
|
||||
NF_CT_TUPLE_U_BLANK(&tuple);
|
||||
memset(&tuple, 0, sizeof(tuple));
|
||||
tuple.src.u3.ip = inet->rcv_saddr;
|
||||
tuple.src.u.tcp.port = inet->sport;
|
||||
tuple.dst.u3.ip = inet->daddr;
|
||||
|
@ -1430,11 +1430,13 @@ static inline unsigned short guess_mtu(unsigned short old_mtu)
|
||||
}
|
||||
|
||||
unsigned short ip_rt_frag_needed(struct net *net, struct iphdr *iph,
|
||||
unsigned short new_mtu)
|
||||
unsigned short new_mtu,
|
||||
struct net_device *dev)
|
||||
{
|
||||
int i;
|
||||
int i, k;
|
||||
unsigned short old_mtu = ntohs(iph->tot_len);
|
||||
struct rtable *rth;
|
||||
int ikeys[2] = { dev->ifindex, 0 };
|
||||
__be32 skeys[2] = { iph->saddr, 0, };
|
||||
__be32 daddr = iph->daddr;
|
||||
unsigned short est_mtu = 0;
|
||||
@ -1442,22 +1444,26 @@ unsigned short ip_rt_frag_needed(struct net *net, struct iphdr *iph,
|
||||
if (ipv4_config.no_pmtu_disc)
|
||||
return 0;
|
||||
|
||||
for (k = 0; k < 2; k++) {
|
||||
for (i = 0; i < 2; i++) {
|
||||
unsigned hash = rt_hash(daddr, skeys[i], 0);
|
||||
unsigned hash = rt_hash(daddr, skeys[i], ikeys[k]);
|
||||
|
||||
rcu_read_lock();
|
||||
for (rth = rcu_dereference(rt_hash_table[hash].chain); rth;
|
||||
rth = rcu_dereference(rth->u.dst.rt_next)) {
|
||||
if (rth->fl.fl4_dst == daddr &&
|
||||
rth->fl.fl4_src == skeys[i] &&
|
||||
rth->rt_dst == daddr &&
|
||||
rth->rt_src == iph->saddr &&
|
||||
rth->fl.iif == 0 &&
|
||||
!(dst_metric_locked(&rth->u.dst, RTAX_MTU)) &&
|
||||
net_eq(dev_net(rth->u.dst.dev), net) &&
|
||||
rth->rt_genid == atomic_read(&rt_genid)) {
|
||||
unsigned short mtu = new_mtu;
|
||||
|
||||
if (rth->fl.fl4_dst != daddr ||
|
||||
rth->fl.fl4_src != skeys[i] ||
|
||||
rth->rt_dst != daddr ||
|
||||
rth->rt_src != iph->saddr ||
|
||||
rth->fl.oif != ikeys[k] ||
|
||||
rth->fl.iif != 0 ||
|
||||
dst_metric_locked(&rth->u.dst, RTAX_MTU) ||
|
||||
!net_eq(dev_net(rth->u.dst.dev), net) ||
|
||||
rth->rt_genid != atomic_read(&rt_genid))
|
||||
continue;
|
||||
|
||||
if (new_mtu < 68 || new_mtu >= old_mtu) {
|
||||
|
||||
/* BSD 4.2 compatibility hack :-( */
|
||||
@ -1483,9 +1489,9 @@ unsigned short ip_rt_frag_needed(struct net *net, struct iphdr *iph,
|
||||
est_mtu = mtu;
|
||||
}
|
||||
}
|
||||
}
|
||||
rcu_read_unlock();
|
||||
}
|
||||
}
|
||||
return est_mtu ? : new_mtu;
|
||||
}
|
||||
|
||||
|
@ -285,13 +285,11 @@ int tcp_is_cwnd_limited(const struct sock *sk, u32 in_flight)
|
||||
if (in_flight >= tp->snd_cwnd)
|
||||
return 1;
|
||||
|
||||
if (!sk_can_gso(sk))
|
||||
return 0;
|
||||
|
||||
left = tp->snd_cwnd - in_flight;
|
||||
if (sysctl_tcp_tso_win_divisor)
|
||||
return left * sysctl_tcp_tso_win_divisor < tp->snd_cwnd;
|
||||
else
|
||||
if (sk_can_gso(sk) &&
|
||||
left * sysctl_tcp_tso_win_divisor < tp->snd_cwnd &&
|
||||
left * tp->mss_cache < sk->sk_gso_max_size)
|
||||
return 1;
|
||||
return left <= tcp_max_burst(tp);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tcp_is_cwnd_limited);
|
||||
|
@ -229,7 +229,8 @@ static void tcp_vegas_cong_avoid(struct sock *sk, u32 ack, u32 in_flight)
|
||||
*/
|
||||
tcp_reno_cong_avoid(sk, ack, in_flight);
|
||||
} else {
|
||||
u32 rtt, target_cwnd, diff;
|
||||
u32 rtt, diff;
|
||||
u64 target_cwnd;
|
||||
|
||||
/* We have enough RTT samples, so, using the Vegas
|
||||
* algorithm, we determine if we should increase or
|
||||
@ -252,8 +253,9 @@ static void tcp_vegas_cong_avoid(struct sock *sk, u32 ack, u32 in_flight)
|
||||
* We keep it as a fixed point number with
|
||||
* V_PARAM_SHIFT bits to the right of the binary point.
|
||||
*/
|
||||
target_cwnd = ((old_wnd * vegas->baseRTT)
|
||||
<< V_PARAM_SHIFT) / rtt;
|
||||
target_cwnd = ((u64)old_wnd * vegas->baseRTT);
|
||||
target_cwnd <<= V_PARAM_SHIFT;
|
||||
do_div(target_cwnd, rtt);
|
||||
|
||||
/* Calculate the difference between the window we had,
|
||||
* and the window we would like to have. This quantity
|
||||
@ -279,7 +281,7 @@ static void tcp_vegas_cong_avoid(struct sock *sk, u32 ack, u32 in_flight)
|
||||
* utilization.
|
||||
*/
|
||||
tp->snd_cwnd = min(tp->snd_cwnd,
|
||||
(target_cwnd >>
|
||||
((u32)target_cwnd >>
|
||||
V_PARAM_SHIFT)+1);
|
||||
|
||||
} else if (tp->snd_cwnd <= tp->snd_ssthresh) {
|
||||
|
@ -133,7 +133,8 @@ static void tcp_veno_cong_avoid(struct sock *sk, u32 ack, u32 in_flight)
|
||||
*/
|
||||
tcp_reno_cong_avoid(sk, ack, in_flight);
|
||||
} else {
|
||||
u32 rtt, target_cwnd;
|
||||
u64 target_cwnd;
|
||||
u32 rtt;
|
||||
|
||||
/* We have enough rtt samples, so, using the Veno
|
||||
* algorithm, we determine the state of the network.
|
||||
@ -141,8 +142,9 @@ static void tcp_veno_cong_avoid(struct sock *sk, u32 ack, u32 in_flight)
|
||||
|
||||
rtt = veno->minrtt;
|
||||
|
||||
target_cwnd = ((tp->snd_cwnd * veno->basertt)
|
||||
<< V_PARAM_SHIFT) / rtt;
|
||||
target_cwnd = (tp->snd_cwnd * veno->basertt);
|
||||
target_cwnd <<= V_PARAM_SHIFT;
|
||||
do_div(target_cwnd, rtt);
|
||||
|
||||
veno->diff = (tp->snd_cwnd << V_PARAM_SHIFT) - target_cwnd;
|
||||
|
||||
|
@ -1089,6 +1089,10 @@ int ipv6_getsockopt(struct sock *sk, int level, int optname,
|
||||
if(level != SOL_IPV6)
|
||||
return -ENOPROTOOPT;
|
||||
|
||||
if (optname == MCAST_MSFILTER)
|
||||
return compat_mc_getsockopt(sk, level, optname, optval, optlen,
|
||||
ipv6_getsockopt);
|
||||
|
||||
err = do_ipv6_getsockopt(sk, level, optname, optval, optlen);
|
||||
#ifdef CONFIG_NETFILTER
|
||||
/* we need to exclude all possible ENOPROTOOPTs except default case */
|
||||
|
@ -298,9 +298,8 @@ ipq_mangle_ipv6(ipq_verdict_msg_t *v, struct nf_queue_entry *e)
|
||||
if (v->data_len > 0xFFFF)
|
||||
return -EINVAL;
|
||||
if (diff > skb_tailroom(e->skb)) {
|
||||
nskb = skb_copy_expand(e->skb, 0,
|
||||
diff - skb_tailroom(e->skb),
|
||||
GFP_ATOMIC);
|
||||
nskb = skb_copy_expand(e->skb, skb_headroom(e->skb),
|
||||
diff, GFP_ATOMIC);
|
||||
if (!nskb) {
|
||||
printk(KERN_WARNING "ip6_queue: OOM "
|
||||
"in mangle, dropping packet\n");
|
||||
|
@ -104,7 +104,7 @@ nf_ct_get_tuple(const struct sk_buff *skb,
|
||||
const struct nf_conntrack_l3proto *l3proto,
|
||||
const struct nf_conntrack_l4proto *l4proto)
|
||||
{
|
||||
NF_CT_TUPLE_U_BLANK(tuple);
|
||||
memset(tuple, 0, sizeof(*tuple));
|
||||
|
||||
tuple->src.l3num = l3num;
|
||||
if (l3proto->pkt_to_tuple(skb, nhoff, tuple) == 0)
|
||||
@ -151,7 +151,7 @@ nf_ct_invert_tuple(struct nf_conntrack_tuple *inverse,
|
||||
const struct nf_conntrack_l3proto *l3proto,
|
||||
const struct nf_conntrack_l4proto *l4proto)
|
||||
{
|
||||
NF_CT_TUPLE_U_BLANK(inverse);
|
||||
memset(inverse, 0, sizeof(*inverse));
|
||||
|
||||
inverse->src.l3num = orig->src.l3num;
|
||||
if (l3proto->invert_tuple(inverse, orig) == 0)
|
||||
|
@ -454,9 +454,8 @@ nfqnl_mangle(void *data, int data_len, struct nf_queue_entry *e)
|
||||
if (data_len > 0xFFFF)
|
||||
return -EINVAL;
|
||||
if (diff > skb_tailroom(e->skb)) {
|
||||
nskb = skb_copy_expand(e->skb, 0,
|
||||
diff - skb_tailroom(e->skb),
|
||||
GFP_ATOMIC);
|
||||
nskb = skb_copy_expand(e->skb, skb_headroom(e->skb),
|
||||
diff, GFP_ATOMIC);
|
||||
if (!nskb) {
|
||||
printk(KERN_WARNING "nf_queue: OOM "
|
||||
"in mangle, dropping packet\n");
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user