Commit 5dcc60b7 authored by Yeasah Pell's avatar Yeasah Pell Committed by David S. Miller

dm9000: add checksum offload support

Add checksum offload support for DM9000A and DM9000B chips.

v2 changes: added a local copy of ip_summed to save IO cycles in dm9000_send_packet
v3 changes: trans_start updating is removed.
Signed-off-by: default avatarYeasah Pell <yeasah@comrex.com>
Signed-off-by: default avatarMike Rapoport <mike@compulab.co.il>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 482d804c
...@@ -92,6 +92,7 @@ typedef struct board_info { ...@@ -92,6 +92,7 @@ typedef struct board_info {
u16 tx_pkt_cnt; u16 tx_pkt_cnt;
u16 queue_pkt_len; u16 queue_pkt_len;
u16 queue_start_addr; u16 queue_start_addr;
u16 queue_ip_summed;
u16 dbug_cnt; u16 dbug_cnt;
u8 io_mode; /* 0:word, 2:byte */ u8 io_mode; /* 0:word, 2:byte */
u8 phy_addr; u8 phy_addr;
...@@ -124,6 +125,10 @@ typedef struct board_info { ...@@ -124,6 +125,10 @@ typedef struct board_info {
struct mii_if_info mii; struct mii_if_info mii;
u32 msg_enable; u32 msg_enable;
int rx_csum;
int can_csum;
int ip_summed;
} board_info_t; } board_info_t;
/* debug code */ /* debug code */
...@@ -460,6 +465,40 @@ static int dm9000_nway_reset(struct net_device *dev) ...@@ -460,6 +465,40 @@ static int dm9000_nway_reset(struct net_device *dev)
return mii_nway_restart(&dm->mii); return mii_nway_restart(&dm->mii);
} }
static uint32_t dm9000_get_rx_csum(struct net_device *dev)
{
board_info_t *dm = to_dm9000_board(dev);
return dm->rx_csum;
}
static int dm9000_set_rx_csum(struct net_device *dev, uint32_t data)
{
board_info_t *dm = to_dm9000_board(dev);
unsigned long flags;
if (dm->can_csum) {
dm->rx_csum = data;
spin_lock_irqsave(&dm->lock, flags);
iow(dm, DM9000_RCSR, dm->rx_csum ? RCSR_CSUM : 0);
spin_unlock_irqrestore(&dm->lock, flags);
return 0;
}
return -EOPNOTSUPP;
}
static int dm9000_set_tx_csum(struct net_device *dev, uint32_t data)
{
board_info_t *dm = to_dm9000_board(dev);
int ret = -EOPNOTSUPP;
if (dm->can_csum)
ret = ethtool_op_set_tx_csum(dev, data);
return ret;
}
static u32 dm9000_get_link(struct net_device *dev) static u32 dm9000_get_link(struct net_device *dev)
{ {
board_info_t *dm = to_dm9000_board(dev); board_info_t *dm = to_dm9000_board(dev);
...@@ -540,6 +579,10 @@ static const struct ethtool_ops dm9000_ethtool_ops = { ...@@ -540,6 +579,10 @@ static const struct ethtool_ops dm9000_ethtool_ops = {
.get_eeprom_len = dm9000_get_eeprom_len, .get_eeprom_len = dm9000_get_eeprom_len,
.get_eeprom = dm9000_get_eeprom, .get_eeprom = dm9000_get_eeprom,
.set_eeprom = dm9000_set_eeprom, .set_eeprom = dm9000_set_eeprom,
.get_rx_csum = dm9000_get_rx_csum,
.set_rx_csum = dm9000_set_rx_csum,
.get_tx_csum = ethtool_op_get_tx_csum,
.set_tx_csum = dm9000_set_tx_csum,
}; };
static void dm9000_show_carrier(board_info_t *db, static void dm9000_show_carrier(board_info_t *db,
...@@ -685,6 +728,9 @@ dm9000_init_dm9000(struct net_device *dev) ...@@ -685,6 +728,9 @@ dm9000_init_dm9000(struct net_device *dev)
/* I/O mode */ /* I/O mode */
db->io_mode = ior(db, DM9000_ISR) >> 6; /* ISR bit7:6 keeps I/O mode */ db->io_mode = ior(db, DM9000_ISR) >> 6; /* ISR bit7:6 keeps I/O mode */
/* Checksum mode */
dm9000_set_rx_csum(dev, db->rx_csum);
/* GPIO0 on pre-activate PHY */ /* GPIO0 on pre-activate PHY */
iow(db, DM9000_GPR, 0); /* REG_1F bit0 activate phyxcer */ iow(db, DM9000_GPR, 0); /* REG_1F bit0 activate phyxcer */
iow(db, DM9000_GPCR, GPCR_GEP_CNTL); /* Let GPIO0 output */ iow(db, DM9000_GPCR, GPCR_GEP_CNTL); /* Let GPIO0 output */
...@@ -743,6 +789,29 @@ static void dm9000_timeout(struct net_device *dev) ...@@ -743,6 +789,29 @@ static void dm9000_timeout(struct net_device *dev)
spin_unlock_irqrestore(&db->lock, flags); spin_unlock_irqrestore(&db->lock, flags);
} }
static void dm9000_send_packet(struct net_device *dev,
int ip_summed,
u16 pkt_len)
{
board_info_t *dm = to_dm9000_board(dev);
/* The DM9000 is not smart enough to leave fragmented packets alone. */
if (dm->ip_summed != ip_summed) {
if (ip_summed == CHECKSUM_NONE)
iow(dm, DM9000_TCCR, 0);
else
iow(dm, DM9000_TCCR, TCCR_IP | TCCR_UDP | TCCR_TCP);
dm->ip_summed = ip_summed;
}
/* Set TX length to DM9000 */
iow(dm, DM9000_TXPLL, pkt_len);
iow(dm, DM9000_TXPLH, pkt_len >> 8);
/* Issue TX polling command */
iow(dm, DM9000_TCR, TCR_TXREQ); /* Cleared after TX complete */
}
/* /*
* Hardware start transmission. * Hardware start transmission.
* Send a packet to media from the upper layer. * Send a packet to media from the upper layer.
...@@ -769,17 +838,11 @@ dm9000_start_xmit(struct sk_buff *skb, struct net_device *dev) ...@@ -769,17 +838,11 @@ dm9000_start_xmit(struct sk_buff *skb, struct net_device *dev)
db->tx_pkt_cnt++; db->tx_pkt_cnt++;
/* TX control: First packet immediately send, second packet queue */ /* TX control: First packet immediately send, second packet queue */
if (db->tx_pkt_cnt == 1) { if (db->tx_pkt_cnt == 1) {
/* Set TX length to DM9000 */ dm9000_send_packet(dev, skb->ip_summed, skb->len);
iow(db, DM9000_TXPLL, skb->len);
iow(db, DM9000_TXPLH, skb->len >> 8);
/* Issue TX polling command */
iow(db, DM9000_TCR, TCR_TXREQ); /* Cleared after TX complete */
dev->trans_start = jiffies; /* save the time stamp */
} else { } else {
/* Second packet */ /* Second packet */
db->queue_pkt_len = skb->len; db->queue_pkt_len = skb->len;
db->queue_ip_summed = skb->ip_summed;
netif_stop_queue(dev); netif_stop_queue(dev);
} }
...@@ -809,12 +872,9 @@ static void dm9000_tx_done(struct net_device *dev, board_info_t *db) ...@@ -809,12 +872,9 @@ static void dm9000_tx_done(struct net_device *dev, board_info_t *db)
dev_dbg(db->dev, "tx done, NSR %02x\n", tx_status); dev_dbg(db->dev, "tx done, NSR %02x\n", tx_status);
/* Queue packet check & send */ /* Queue packet check & send */
if (db->tx_pkt_cnt > 0) { if (db->tx_pkt_cnt > 0)
iow(db, DM9000_TXPLL, db->queue_pkt_len); dm9000_send_packet(dev, db->queue_ip_summed,
iow(db, DM9000_TXPLH, db->queue_pkt_len >> 8); db->queue_pkt_len);
iow(db, DM9000_TCR, TCR_TXREQ);
dev->trans_start = jiffies;
}
netif_wake_queue(dev); netif_wake_queue(dev);
} }
} }
...@@ -846,14 +906,14 @@ dm9000_rx(struct net_device *dev) ...@@ -846,14 +906,14 @@ dm9000_rx(struct net_device *dev)
rxbyte = readb(db->io_data); rxbyte = readb(db->io_data);
/* Status check: this byte must be 0 or 1 */ /* Status check: this byte must be 0 or 1 */
if (rxbyte > DM9000_PKT_RDY) { if (rxbyte & DM9000_PKT_ERR) {
dev_warn(db->dev, "status check fail: %d\n", rxbyte); dev_warn(db->dev, "status check fail: %d\n", rxbyte);
iow(db, DM9000_RCR, 0x00); /* Stop Device */ iow(db, DM9000_RCR, 0x00); /* Stop Device */
iow(db, DM9000_ISR, IMR_PAR); /* Stop INT request */ iow(db, DM9000_ISR, IMR_PAR); /* Stop INT request */
return; return;
} }
if (rxbyte != DM9000_PKT_RDY) if (!(rxbyte & DM9000_PKT_RDY))
return; return;
/* A packet ready now & Get status/length */ /* A packet ready now & Get status/length */
...@@ -914,6 +974,12 @@ dm9000_rx(struct net_device *dev) ...@@ -914,6 +974,12 @@ dm9000_rx(struct net_device *dev)
/* Pass to upper layer */ /* Pass to upper layer */
skb->protocol = eth_type_trans(skb, dev); skb->protocol = eth_type_trans(skb, dev);
if (db->rx_csum) {
if ((((rxbyte & 0x1c) << 3) & rxbyte) == 0)
skb->ip_summed = CHECKSUM_UNNECESSARY;
else
skb->ip_summed = CHECKSUM_NONE;
}
netif_rx(skb); netif_rx(skb);
dev->stats.rx_packets++; dev->stats.rx_packets++;
...@@ -922,7 +988,7 @@ dm9000_rx(struct net_device *dev) ...@@ -922,7 +988,7 @@ dm9000_rx(struct net_device *dev)
(db->dumpblk)(db->io_data, RxLen); (db->dumpblk)(db->io_data, RxLen);
} }
} while (rxbyte == DM9000_PKT_RDY); } while (rxbyte & DM9000_PKT_RDY);
} }
static irqreturn_t dm9000_interrupt(int irq, void *dev_id) static irqreturn_t dm9000_interrupt(int irq, void *dev_id)
...@@ -1349,6 +1415,13 @@ dm9000_probe(struct platform_device *pdev) ...@@ -1349,6 +1415,13 @@ dm9000_probe(struct platform_device *pdev)
db->type = TYPE_DM9000E; db->type = TYPE_DM9000E;
} }
/* dm9000a/b are capable of hardware checksum offload */
if (db->type == TYPE_DM9000A || db->type == TYPE_DM9000B) {
db->can_csum = 1;
db->rx_csum = 1;
ndev->features |= NETIF_F_IP_CSUM;
}
/* from this point we assume that we have found a DM9000 */ /* from this point we assume that we have found a DM9000 */
/* driver system function */ /* driver system function */
......
...@@ -45,6 +45,10 @@ ...@@ -45,6 +45,10 @@
#define DM9000_CHIPR 0x2C #define DM9000_CHIPR 0x2C
#define DM9000_SMCR 0x2F #define DM9000_SMCR 0x2F
#define DM9000_ETXCSR 0x30
#define DM9000_TCCR 0x31
#define DM9000_RCSR 0x32
#define CHIPR_DM9000A 0x19 #define CHIPR_DM9000A 0x19
#define CHIPR_DM9000B 0x1B #define CHIPR_DM9000B 0x1B
...@@ -131,7 +135,21 @@ ...@@ -131,7 +135,21 @@
#define GPCR_GEP_CNTL (1<<0) #define GPCR_GEP_CNTL (1<<0)
#define TCCR_IP (1<<0)
#define TCCR_TCP (1<<1)
#define TCCR_UDP (1<<2)
#define RCSR_UDP_BAD (1<<7)
#define RCSR_TCP_BAD (1<<6)
#define RCSR_IP_BAD (1<<5)
#define RCSR_UDP (1<<4)
#define RCSR_TCP (1<<3)
#define RCSR_IP (1<<2)
#define RCSR_CSUM (1<<1)
#define RCSR_DISCARD (1<<0)
#define DM9000_PKT_RDY 0x01 /* Packet ready to receive */ #define DM9000_PKT_RDY 0x01 /* Packet ready to receive */
#define DM9000_PKT_ERR 0x02
#define DM9000_PKT_MAX 1536 /* Received packet max size */ #define DM9000_PKT_MAX 1536 /* Received packet max size */
/* DM9000A / DM9000B definitions */ /* DM9000A / DM9000B definitions */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment