patch-2.4.4 linux/drivers/net/sunhme.c
Next file: linux/drivers/net/sunhme.h
Previous file: linux/drivers/net/sungem.h
Back to the patch index
Back to the overall index
- Lines: 589
- Date:
Thu Apr 26 22:17:26 2001
- Orig file:
v2.4.3/linux/drivers/net/sunhme.c
- Orig date:
Tue Feb 13 13:15:05 2001
diff -u --recursive --new-file v2.4.3/linux/drivers/net/sunhme.c linux/drivers/net/sunhme.c
@@ -1,4 +1,4 @@
-/* $Id: sunhme.c,v 1.105 2000/12/05 02:00:36 anton Exp $
+/* $Id: sunhme.c,v 1.117 2001/04/19 22:32:41 davem Exp $
* sunhme.c: Sparc HME/BigMac 10/100baseT half/full duplex auto switching,
* auto carrier detecting ethernet driver. Also known as the
* "Happy Meal Ethernet" found on SunSwift SBUS cards.
@@ -13,7 +13,7 @@
* argument : macaddr=0x00,0x10,0x20,0x30,0x40,0x50
*/
-static char *version =
+static char version[] =
"sunhme.c:v1.99 12/Sep/99 David S. Miller (davem@redhat.com)\n";
#include <linux/module.h>
@@ -73,14 +73,14 @@
/* accept MAC address of the form macaddr=0x08,0x00,0x20,0x30,0x40,0x50 */
MODULE_PARM(macaddr, "6i");
-static struct happy_meal *root_happy_dev = NULL;
+static struct happy_meal *root_happy_dev;
#ifdef CONFIG_SBUS
-static struct quattro *qfe_sbus_list = NULL;
+static struct quattro *qfe_sbus_list;
#endif
#ifdef CONFIG_PCI
-static struct quattro *qfe_pci_list = NULL;
+static struct quattro *qfe_pci_list;
#endif
#undef HMEDEBUG
@@ -102,7 +102,7 @@
};
#define TX_LOG_LEN 128
static struct hme_tx_logent tx_log[TX_LOG_LEN];
-static int txlog_cur_entry = 0;
+static int txlog_cur_entry;
static __inline__ void tx_add_log(struct happy_meal *hp, unsigned int a, unsigned int s)
{
struct hme_tx_logent *tlp;
@@ -1277,12 +1277,23 @@
struct sk_buff *skb = hp->tx_skbs[i];
struct happy_meal_txd *txd;
u32 dma_addr;
+ int frag;
- txd = &hp->happy_block->happy_meal_txd[i];
- dma_addr = hme_read_desc32(hp, &txd->tx_addr);
- hme_dma_unmap(hp, dma_addr, skb->len, DMA_TODEVICE);
- dev_kfree_skb_any(skb);
hp->tx_skbs[i] = NULL;
+
+ for (frag = 0; frag <= skb_shinfo(skb)->nr_frags; frag++) {
+ txd = &hp->happy_block->happy_meal_txd[i];
+ dma_addr = hme_read_desc32(hp, &txd->tx_addr);
+ hme_dma_unmap(hp, dma_addr,
+ (hme_read_desc32(hp, &txd->tx_flags)
+ & TXFLAG_SIZE),
+ DMA_TODEVICE);
+
+ if (frag != skb_shinfo(skb)->nr_frags)
+ i++;
+ }
+
+ dev_kfree_skb_any(skb);
}
}
}
@@ -1842,7 +1853,7 @@
if (status & GREG_STAT_NORXD) {
/* This is harmless, it just means the system is
- * quite loaded and the incomming packet rate was
+ * quite loaded and the incoming packet rate was
* faster than the interrupt handler could keep up
* with.
*/
@@ -1958,23 +1969,40 @@
TXD(("TX<"));
while (elem != hp->tx_new) {
struct sk_buff *skb;
- u32 flags, dma_addr;
+ u32 flags, dma_addr, dma_len;
+ int frag;
TXD(("[%d]", elem));
this = &txbase[elem];
flags = hme_read_desc32(hp, &this->tx_flags);
if (flags & TXFLAG_OWN)
break;
- dma_addr = hme_read_desc32(hp, &this->tx_addr);
skb = hp->tx_skbs[elem];
- hme_dma_unmap(hp, dma_addr, skb->len, DMA_TODEVICE);
+ if (skb_shinfo(skb)->nr_frags) {
+ int last;
+
+ last = elem + skb_shinfo(skb)->nr_frags;
+ last &= (TX_RING_SIZE - 1);
+ flags = hme_read_desc32(hp, &txbase[last].tx_flags);
+ if (flags & TXFLAG_OWN)
+ break;
+ }
hp->tx_skbs[elem] = NULL;
hp->net_stats.tx_bytes += skb->len;
- dev_kfree_skb_irq(skb);
+ for (frag = 0; frag <= skb_shinfo(skb)->nr_frags; frag++) {
+ dma_addr = hme_read_desc32(hp, &this->tx_addr);
+ dma_len = hme_read_desc32(hp, &this->tx_flags);
+
+ dma_len &= TXFLAG_SIZE;
+ hme_dma_unmap(hp, dma_addr, dma_len, DMA_TODEVICE);
+ elem = NEXT_TX(elem);
+ this = &txbase[elem];
+ }
+
+ dev_kfree_skb_irq(skb);
hp->net_stats.tx_packets++;
- elem = NEXT_TX(elem);
}
hp->tx_old = elem;
TXD((">"));
@@ -2079,10 +2107,8 @@
}
/* This card is _fucking_ hot... */
- if (!(csum ^ 0xffff))
- skb->ip_summed = CHECKSUM_UNNECESSARY;
- else
- skb->ip_summed = CHECKSUM_NONE;
+ skb->csum = (csum ^ 0xffff);
+ skb->ip_summed = CHECKSUM_HW;
RXD(("len=%d csum=%4x]", len, csum));
skb->protocol = eth_type_trans(skb, dev);
@@ -2104,7 +2130,7 @@
static void happy_meal_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
struct net_device *dev = (struct net_device *) dev_id;
- struct happy_meal *hp = (struct happy_meal *) dev->priv;
+ struct happy_meal *hp = dev->priv;
u32 happy_status = hme_read32(hp, hp->gregs + GREG_STAT);
HMD(("happy_meal_interrupt: status=%08x ", happy_status));
@@ -2141,7 +2167,7 @@
for (i = 0; i < 4; i++) {
struct net_device *dev = qp->happy_meals[i];
- struct happy_meal *hp = (struct happy_meal *) dev->priv;
+ struct happy_meal *hp = dev->priv;
u32 happy_status = hme_read32(hp, hp->gregs + GREG_STAT);
HMD(("quattro_interrupt: status=%08x ", happy_status));
@@ -2179,8 +2205,7 @@
static int happy_meal_open(struct net_device *dev)
{
- struct happy_meal *hp = (struct happy_meal *) dev->priv;
- int res;
+ struct happy_meal *hp = dev->priv;
HMD(("happy_meal_open: "));
@@ -2204,16 +2229,12 @@
}
HMD(("to happy_meal_init\n"));
- res = happy_meal_init(hp, 0);
- if (!res) {
- MOD_INC_USE_COUNT;
- }
- return res;
+ return happy_meal_init(hp, 0);
}
static int happy_meal_close(struct net_device *dev)
{
- struct happy_meal *hp = (struct happy_meal *) dev->priv;
+ struct happy_meal *hp = dev->priv;
happy_meal_stop(hp, hp->gregs);
happy_meal_clean_rings(hp);
@@ -2226,9 +2247,8 @@
* time and never unregister.
*/
if ((hp->happy_flags & (HFLAG_QUATTRO|HFLAG_PCI)) != HFLAG_QUATTRO)
- free_irq(dev->irq, (void *)dev);
+ free_irq(dev->irq, dev);
- MOD_DEC_USE_COUNT;
return 0;
}
@@ -2241,7 +2261,7 @@
#ifdef CONFIG_SBUS
static void happy_meal_tx_timeout(struct net_device *dev)
{
- struct happy_meal *hp = (struct happy_meal *) dev->priv;
+ struct happy_meal *hp = dev->priv;
printk (KERN_ERR "%s: transmit timed out, resetting\n", dev->name);
tx_dump_log();
@@ -2256,22 +2276,79 @@
static int happy_meal_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
- struct happy_meal *hp = (struct happy_meal *) dev->priv;
- int len, entry;
- u32 mapping;
-
- len = skb->len;
- mapping = hme_dma_map(hp, skb->data, len, DMA_TODEVICE);
+ struct happy_meal *hp = dev->priv;
+ int entry;
+ u32 tx_flags;
+
+ tx_flags = TXFLAG_OWN;
+ if (skb->ip_summed == CHECKSUM_HW) {
+ u32 csum_start_off, csum_stuff_off;
+
+ csum_start_off = (u32) (skb->h.raw - skb->data);
+ csum_stuff_off = (u32) ((skb->h.raw + skb->csum) - skb->data);
+
+ tx_flags = (TXFLAG_OWN | TXFLAG_CSENABLE |
+ ((csum_start_off << 14) & TXFLAG_CSBUFBEGIN) |
+ ((csum_stuff_off << 20) & TXFLAG_CSLOCATION));
+ }
spin_lock_irq(&hp->happy_lock);
+ if (TX_BUFFS_AVAIL(hp) <= (skb_shinfo(skb)->nr_frags + 1)) {
+ netif_stop_queue(dev);
+ spin_unlock_irq(&hp->happy_lock);
+ return 1;
+ }
+
entry = hp->tx_new;
SXD(("SX<l[%d]e[%d]>", len, entry));
hp->tx_skbs[entry] = skb;
- hme_write_txd(hp, &hp->happy_block->happy_meal_txd[entry],
- (TXFLAG_OWN | TXFLAG_SOP | TXFLAG_EOP | (len & TXFLAG_SIZE)),
- mapping);
- hp->tx_new = NEXT_TX(entry);
+
+ if (skb_shinfo(skb)->nr_frags == 0) {
+ u32 mapping, len;
+
+ len = skb->len;
+ mapping = hme_dma_map(hp, skb->data, len, DMA_TODEVICE);
+ tx_flags |= (TXFLAG_SOP | TXFLAG_EOP);
+ hme_write_txd(hp, &hp->happy_block->happy_meal_txd[entry],
+ (tx_flags | (len & TXFLAG_SIZE)),
+ mapping);
+ entry = NEXT_TX(entry);
+ } else {
+ u32 first_len, first_mapping;
+ int frag, first_entry = entry;
+
+ /* We must give this initial chunk to the device last.
+ * Otherwise we could race with the device.
+ */
+ first_len = skb->len - skb->data_len;
+ first_mapping = hme_dma_map(hp, skb->data, first_len, DMA_TODEVICE);
+ entry = NEXT_TX(entry);
+
+ for (frag = 0; frag < skb_shinfo(skb)->nr_frags; frag++) {
+ skb_frag_t *this_frag = &skb_shinfo(skb)->frags[frag];
+ u32 len, mapping, this_txflags;
+
+ len = this_frag->size;
+ mapping = hme_dma_map(hp,
+ ((void *) page_address(this_frag->page) +
+ this_frag->page_offset),
+ len, DMA_TODEVICE);
+ this_txflags = tx_flags;
+ if (frag == skb_shinfo(skb)->nr_frags - 1)
+ this_txflags |= TXFLAG_EOP;
+ hme_write_txd(hp, &hp->happy_block->happy_meal_txd[entry],
+ (this_txflags | (len & TXFLAG_SIZE)),
+ mapping);
+ entry = NEXT_TX(entry);
+ }
+ hme_write_txd(hp, &hp->happy_block->happy_meal_txd[first_entry],
+ (tx_flags | TXFLAG_SOP | (first_len & TXFLAG_SIZE)),
+ first_mapping);
+ }
+
+ hp->tx_new = entry;
+
if (TX_BUFFS_AVAIL(hp) <= 0)
netif_stop_queue(dev);
@@ -2288,7 +2365,7 @@
static struct net_device_stats *happy_meal_get_stats(struct net_device *dev)
{
- struct happy_meal *hp = (struct happy_meal *) dev->priv;
+ struct happy_meal *hp = dev->priv;
happy_meal_get_counters(hp, hp->bigmacregs);
return &hp->net_stats;
@@ -2296,7 +2373,7 @@
static void happy_meal_set_multicast(struct net_device *dev)
{
- struct happy_meal *hp = (struct happy_meal *) dev->priv;
+ struct happy_meal *hp = dev->priv;
unsigned long bregs = hp->bigmacregs;
struct dev_mc_list *dmi = dev->mc_list;
char *addrs;
@@ -2355,7 +2432,7 @@
static int happy_meal_ioctl(struct net_device *dev,
struct ifreq *rq, int cmd)
{
- struct happy_meal *hp = (struct happy_meal *) dev->priv;
+ struct happy_meal *hp = dev->priv;
struct ethtool_cmd *ep_user = (struct ethtool_cmd *) rq->ifr_data;
struct ethtool_cmd ecmd;
@@ -2429,7 +2506,7 @@
return -EOPNOTSUPP;
}
-static int hme_version_printed = 0;
+static int hme_version_printed;
#ifdef CONFIG_SBUS
void __init quattro_get_ranges(struct quattro *qp)
@@ -2480,27 +2557,23 @@
struct sbus_bus *sbus;
struct sbus_dev *sdev;
struct quattro *qp;
+ int i;
if (qfe_sbus_list == NULL)
goto found;
for (qp = qfe_sbus_list; qp != NULL; qp = qp->next) {
- for (sdev = qp->quattro_dev;
- sdev != NULL;
- sdev = sdev->next) {
+ for (i = 0, sdev = qp->quattro_dev;
+ (sdev != NULL) && (i < 4);
+ sdev = sdev->next, i++) {
if (sdev == goal_sdev)
return qp;
}
}
for_each_sbus(sbus) {
for_each_sbusdev(sdev, sbus) {
- if (sdev->child != NULL) {
- struct sbus_dev *p;
-
- for (p = sdev->child; p != NULL; p = p->next)
- if (p == goal_sdev)
- goto found;
- }
+ if (sdev == goal_sdev)
+ goto found;
}
}
@@ -2578,12 +2651,11 @@
#endif /* CONFIG_PCI */
#ifdef CONFIG_SBUS
-static int __init happy_meal_sbus_init(struct net_device *dev,
- struct sbus_dev *sdev,
- int is_qfe)
+static int __init happy_meal_sbus_init(struct sbus_dev *sdev, int is_qfe)
{
struct quattro *qp = NULL;
struct happy_meal *hp;
+ struct net_device *dev;
int i, qfe_slot = -1;
if (is_qfe) {
@@ -2596,13 +2668,12 @@
if (qfe_slot == 4)
return -ENODEV;
}
- if (dev == NULL) {
- dev = init_etherdev(0, sizeof(struct happy_meal));
- } else {
- dev->priv = kmalloc(sizeof(struct happy_meal), GFP_KERNEL);
- if (dev->priv == NULL)
- return -ENOMEM;
- }
+
+ dev = init_etherdev(NULL, sizeof(struct happy_meal));
+ if (!dev)
+ return -ENOMEM;
+ SET_MODULE_OWNER(dev);
+
if (hme_version_printed++ == 0)
printk(KERN_INFO "%s", version);
@@ -2637,7 +2708,7 @@
dev->dev_addr[i], i == 5 ? ' ' : ':');
printk("\n");
- hp = (struct happy_meal *) dev->priv;
+ hp = dev->priv;
memset(hp, 0, sizeof(*hp));
hp->happy_dev = sdev;
@@ -2733,6 +2804,9 @@
dev->watchdog_timeo = 5*HZ;
dev->do_ioctl = &happy_meal_ioctl;
+ /* Happy Meal can do it all... */
+ dev->features |= NETIF_F_SG | NETIF_F_HW_CSUM;
+
dev->irq = sdev->irqs[0];
#if defined(CONFIG_SBUS) && defined(CONFIG_PCI)
@@ -2766,7 +2840,7 @@
#endif
#ifdef CONFIG_PCI
-static int __init happy_meal_pci_init(struct net_device *dev, struct pci_dev *pdev)
+static int __init happy_meal_pci_init(struct pci_dev *pdev)
{
struct quattro *qp = NULL;
#ifdef __sparc__
@@ -2774,6 +2848,7 @@
int node;
#endif
struct happy_meal *hp;
+ struct net_device *dev;
unsigned long hpreg_base;
int i, qfe_slot = -1;
char prom_name[64];
@@ -2803,13 +2878,12 @@
if (qfe_slot == 4)
return -ENODEV;
}
- if (dev == NULL) {
- dev = init_etherdev(0, sizeof(struct happy_meal));
- } else {
- dev->priv = kmalloc(sizeof(struct happy_meal), GFP_KERNEL);
- if (dev->priv == NULL)
- return -ENOMEM;
- }
+
+ dev = init_etherdev(NULL, sizeof(struct happy_meal));
+ if (!dev)
+ return -ENOMEM;
+ SET_MODULE_OWNER(dev);
+
if (hme_version_printed++ == 0)
printk(KERN_INFO "%s", version);
@@ -2856,7 +2930,10 @@
printk(KERN_ERR "happymeal(PCI): Cannot find proper PCI device base address.\n");
return -ENODEV;
}
- hpreg_base = (unsigned long) ioremap(hpreg_base, 0x8000);
+ if ((hpreg_base = (unsigned long) ioremap(hpreg_base, 0x8000)) == 0) {
+ printk(KERN_ERR "happymeal(PCI): Unable to remap card memory.\n");
+ return -ENODEV;
+ }
for (i = 0; i < 6; i++) {
if (macaddr[i] != 0)
@@ -2941,6 +3018,9 @@
dev->irq = pdev->irq;
dev->dma = 0;
+ /* Happy Meal can do it all... */
+ dev->features |= NETIF_F_SG | NETIF_F_HW_CSUM;
+
#if defined(CONFIG_SBUS) && defined(CONFIG_PCI)
/* Hook up PCI register/dma accessors. */
hp->read_desc32 = pci_hme_read_desc32;
@@ -2972,25 +3052,29 @@
#endif
#ifdef CONFIG_SBUS
-static int __init happy_meal_sbus_probe(struct net_device *dev)
+static int __init happy_meal_sbus_probe(void)
{
struct sbus_bus *sbus;
struct sbus_dev *sdev;
int cards = 0;
+ char model[128];
for_each_sbus(sbus) {
for_each_sbusdev(sdev, sbus) {
char *name = sdev->prom_name;
- if (cards)
- dev = NULL;
if (!strcmp(name, "SUNW,hme")) {
cards++;
- happy_meal_sbus_init(dev, sdev, 0);
+ prom_getstring(sdev->prom_node, "model",
+ model, sizeof(model));
+ if (!strcmp(model, "SUNW,sbus-qfe"))
+ happy_meal_sbus_init(sdev, 1);
+ else
+ happy_meal_sbus_init(sdev, 0);
} else if (!strcmp(name, "qfe") ||
!strcmp(name, "SUNW,qfe")) {
cards++;
- happy_meal_sbus_init(dev, sdev, 1);
+ happy_meal_sbus_init(sdev, 1);
}
}
}
@@ -3001,7 +3085,7 @@
#endif
#ifdef CONFIG_PCI
-static int __init happy_meal_pci_probe(struct net_device *dev)
+static int __init happy_meal_pci_probe(void)
{
struct pci_dev *pdev = NULL;
int cards = 0;
@@ -3010,10 +3094,8 @@
PCI_DEVICE_ID_SUN_HAPPYMEAL, pdev)) != NULL) {
if (pci_enable_device(pdev))
continue;
- if (cards)
- dev = NULL;
cards++;
- happy_meal_pci_init(dev, pdev);
+ happy_meal_pci_init(pdev);
}
return cards;
}
@@ -3021,7 +3103,6 @@
static int __init happy_meal_probe(void)
{
- struct net_device *dev = NULL;
static int called = 0;
int cards;
@@ -3033,12 +3114,10 @@
cards = 0;
#ifdef CONFIG_SBUS
- cards += happy_meal_sbus_probe(dev);
- if (cards != 0)
- dev = NULL;
+ cards += happy_meal_sbus_probe();
#endif
#ifdef CONFIG_PCI
- cards += happy_meal_pci_probe(dev);
+ cards += happy_meal_pci_probe();
#endif
if (!cards)
return -ENODEV;
@@ -3048,12 +3127,23 @@
static void __exit happy_meal_cleanup_module(void)
{
+#ifdef CONFIG_SBUS
+ struct quattro *last_seen_qfe = NULL;
+#endif
+
while (root_happy_dev) {
struct happy_meal *hp = root_happy_dev;
struct happy_meal *next = root_happy_dev->next_module;
#ifdef CONFIG_SBUS
if (!(hp->happy_flags & HFLAG_PCI)) {
+ if (hp->happy_flags & HFLAG_QUATTRO) {
+ if (hp->qfe_parent != last_seen_qfe) {
+ free_irq(hp->dev->irq, hp->qfe_parent);
+ last_seen_qfe = hp->qfe_parent;
+ }
+ }
+
sbus_iounmap(hp->gregs, GREG_REG_SIZE);
sbus_iounmap(hp->etxregs, ETX_REG_SIZE);
sbus_iounmap(hp->erxregs, ERX_REG_SIZE);
@@ -3071,6 +3161,7 @@
PAGE_SIZE,
hp->happy_block,
hp->hblock_dvma);
+ iounmap((void *)hp->gregs);
}
#endif
unregister_netdev(hp->dev);
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)