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

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)