patch-2.4.22 linux-2.4.22/drivers/acorn/net/etherh.c

Next file: linux-2.4.22/drivers/acorn/scsi/arxescsi.c
Previous file: linux-2.4.22/drivers/acorn/net/ether3.c
Back to the patch index
Back to the overall index

diff -urN linux-2.4.21/drivers/acorn/net/etherh.c linux-2.4.22/drivers/acorn/net/etherh.c
@@ -1,7 +1,7 @@
 /*
  *  linux/drivers/acorn/net/etherh.c
  *
- *  Copyright (C) 2000 Russell King
+ *  Copyright (C) 2000-2002 Russell King
  *
  * 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
@@ -23,6 +23,7 @@
  *  12-10-1999  CK/TEW		EtherM driver first release
  *  21-12-2000	TTC		EtherH/EtherM integration
  *  25-12-2000	RMK	1.08	Clean integration of EtherM into this driver.
+ *  03-01-2002	RMK	1.09	Always enable IRQs if we're in the nic slot.
  */
 
 #include <linux/module.h>
@@ -64,13 +65,18 @@
 	{ 0xffff,   0xffff }
 };
 
+struct etherh_priv {
+	unsigned int	id;
+	unsigned int	ctrl_port;
+	unsigned int	ctrl;
+};
 
 MODULE_AUTHOR("Russell King");
 MODULE_DESCRIPTION("EtherH/EtherM driver");
 MODULE_LICENSE("GPL");
 
 static char version[] __initdata =
-	"EtherH/EtherM Driver (c) 2000 Russell King v1.08\n";
+	"EtherH/EtherM Driver (c) 2002 Russell King v1.09\n";
 
 #define ETHERH500_DATAPORT	0x200	/* MEMC */
 #define ETHERH500_NS8390	0x000	/* MEMC */
@@ -97,18 +103,61 @@
 #define ETHERM_TX_START_PAGE	64
 #define ETHERM_STOP_PAGE	127
 
-/* --------------------------------------------------------------------------- */
+/* ------------------------------------------------------------------------ */
+
+static inline void etherh_set_ctrl(struct etherh_priv *eh, unsigned int mask)
+{
+	eh->ctrl |= mask;
+	outb(eh->ctrl, eh->ctrl_port);
+}
+
+static inline void etherh_clr_ctrl(struct etherh_priv *eh, unsigned int mask)
+{
+	eh->ctrl &= ~mask;
+	outb(eh->ctrl, eh->ctrl_port);
+}
+
+static inline unsigned int etherh_get_stat(struct etherh_priv *eh)
+{
+	return inb(eh->ctrl_port);
+}
+
+
+
+
+static void etherh_irq_enable(ecard_t *ec, int irqnr)
+{
+	struct etherh_priv *eh = ec->irq_data;
+
+	etherh_set_ctrl(eh, ETHERH_CP_IE);
+}
+
+static void etherh_irq_disable(ecard_t *ec, int irqnr)
+{
+	struct etherh_priv *eh = ec->irq_data;
+
+	etherh_clr_ctrl(eh, ETHERH_CP_IE);
+}
+
+static expansioncard_ops_t etherh_ops = {
+	irqenable:	etherh_irq_enable,
+	irqdisable:	etherh_irq_disable,
+};
+
+
+
 
 static void
 etherh_setif(struct net_device *dev)
 {
 	struct ei_device *ei_local = (struct ei_device *) dev->priv;
+	struct etherh_priv *eh = (struct etherh_priv *)dev->rmem_start;
 	unsigned long addr, flags;
 
 	save_flags_cli(flags);
 
 	/* set the interface type */
-	switch (dev->mem_end) {
+	switch (eh->id) {
 	case PROD_I3_ETHERLAN600:
 	case PROD_I3_ETHERLAN600A:
 		addr = dev->base_addr + EN0_RCNTHI;
@@ -124,14 +173,13 @@
 		break;
 
 	case PROD_I3_ETHERLAN500:
-		addr = dev->rmem_start;
-
 		switch (dev->if_port) {
 		case IF_PORT_10BASE2:
-			outb(inb(addr) & ~ETHERH_CP_IF, addr);
+			etherh_clr_ctrl(eh, ETHERH_CP_IF);
 			break;
+
 		case IF_PORT_10BASET:
-			outb(inb(addr) | ETHERH_CP_IF, addr);
+			etherh_set_ctrl(eh, ETHERH_CP_IF);
 			break;
 		}
 		break;
@@ -147,9 +195,10 @@
 etherh_getifstat(struct net_device *dev)
 {
 	struct ei_device *ei_local = (struct ei_device *) dev->priv;
+	struct etherh_priv *eh = (struct etherh_priv *)dev->rmem_start;
 	int stat = 0;
 
-	switch (dev->mem_end) {
+	switch (eh->id) {
 	case PROD_I3_ETHERLAN600:
 	case PROD_I3_ETHERLAN600A:
 		switch (dev->if_port) {
@@ -168,7 +217,7 @@
 			stat = 1;
 			break;
 		case IF_PORT_10BASET:
-			stat = inb(dev->rmem_start) & ETHERH_CP_HEARTBEAT;
+			stat = etherh_get_stat(eh) & ETHERH_CP_HEARTBEAT;
 			break;
 		}
 		break;
@@ -251,7 +300,13 @@
 		return;
 	}
 
-	ei_local->dmaing |= 1;
+	/*
+	 * Make sure we have a round number of bytes if we're in word mode.
+	 */
+	if (count & 1 && ei_local->word16)
+		count++;
+
+	ei_local->dmaing = 1;
 
 	addr = dev->base_addr;
 	dma_addr = dev->mem_start;
@@ -291,7 +346,7 @@
 		}
 
 	outb (ENISR_RDC, addr + EN0_ISR);
-	ei_local->dmaing &= ~1;
+	ei_local->dmaing = 0;
 }
 
 /*
@@ -311,7 +366,7 @@
 		return;
 	}
 
-	ei_local->dmaing |= 1;
+	ei_local->dmaing = 1;
 
 	addr = dev->base_addr;
 	dma_addr = dev->mem_start;
@@ -332,7 +387,7 @@
 		insb (dma_addr, buf, count);
 
 	outb (ENISR_RDC, addr + EN0_ISR);
-	ei_local->dmaing &= ~1;
+	ei_local->dmaing = 0;
 }
 
 /*
@@ -351,7 +406,7 @@
 		return;
 	}
 
-	ei_local->dmaing |= 1;
+	ei_local->dmaing = 1;
 
 	addr = dev->base_addr;
 	dma_addr = dev->mem_start;
@@ -369,7 +424,7 @@
 		insb (dma_addr, hdr, sizeof (*hdr));
 
 	outb (ENISR_RDC, addr + EN0_ISR);
-	ei_local->dmaing &= ~1;
+	ei_local->dmaing = 0;
 }
 
 /*
@@ -385,6 +440,12 @@
 {
 	struct ei_device *ei_local = (struct ei_device *) dev->priv;
 
+	if (!is_valid_ether_addr(dev->dev_addr)) {
+		printk(KERN_WARNING "%s: invalid ethernet address\n",
+			dev->name);
+		return -EINVAL;
+	}
+
 	if (request_irq(dev->irq, ei_interrupt, 0, dev->name, dev))
 		return -EAGAIN;
 
@@ -427,22 +488,22 @@
 	return 0;
 }
 
-static void etherh_irq_enable(ecard_t *ec, int irqnr)
+static int
+etherh_set_mac_address(struct net_device *dev, void *p)
 {
-	unsigned int ctrl_addr = (unsigned int)ec->irq_data;
-	outb(inb(ctrl_addr) | ETHERH_CP_IE, ctrl_addr);
-}
+	struct sockaddr *addr = p;
 
-static void etherh_irq_disable(ecard_t *ec, int irqnr)
-{
-	unsigned int ctrl_addr = (unsigned int)ec->irq_data;
-	outb(inb(ctrl_addr) & ~ETHERH_CP_IE, ctrl_addr);
-}
+	if (netif_running(dev))
+		return -EBUSY;
 
-static expansioncard_ops_t etherh_ops = {
-	irqenable:	etherh_irq_enable,
-	irqdisable:	etherh_irq_disable,
-};
+	memcpy(dev->dev_addr, addr->sa_data, dev->addr_len);
+
+	/*
+	 * We'll set the MAC address on the chip when we open it.
+	 */
+
+	return 0;
+}
 
 /*
  * Initialisation
@@ -460,11 +521,13 @@
  * Read the ethernet address string from the on board rom.
  * This is an ascii string...
  */
-static int __init etherh_addr(char *addr, struct expansion_card *ec)
+static void __init etherh_addr(char *addr, struct expansion_card *ec)
 {
 	struct in_chunk_dir cd;
 	char *s;
 	
+	memset(addr, 0, 6);
+
 	if (ecard_readchunk(&cd, ec, 0xf5, 0) && (s = strchr(cd.d.string, '('))) {
 		int i;
 		for (i = 0; i < 6; i++) {
@@ -472,31 +535,25 @@
 			if (*s != (i == 5? ')' : ':'))
 				break;
 		}
-		if (i == 6)
-			return 0;
 	}
-	return -ENODEV;
 }
 
 /*
  * Create an ethernet address from the system serial number.
  */
-static int __init etherm_addr(char *addr)
+static void __init etherm_addr(char *addr)
 {
 	unsigned int serial;
 
-	if (system_serial_low == 0 && system_serial_high == 0)
-		return -ENODEV;
-
 	serial = system_serial_low | system_serial_high;
-
-	addr[0] = 0;
-	addr[1] = 0;
-	addr[2] = 0xa4;
-	addr[3] = 0x10 + (serial >> 24);
-	addr[4] = serial >> 16;
-	addr[5] = serial >> 8;
-	return 0;
+	if (serial != 0) {
+		addr[0] = 0;
+		addr[1] = 0;
+		addr[2] = 0xa4;
+		addr[3] = 0x10 + (serial >> 24);
+		addr[4] = serial >> 16;
+		addr[5] = serial >> 8;
+	}
 }
 
 static u32 etherh_regoffsets[16];
@@ -506,6 +563,7 @@
 {
 	struct ei_device *ei_local;
 	struct net_device *dev;
+	struct etherh_priv *eh;
 	const char *dev_type;
 	int i, size;
 
@@ -517,42 +575,50 @@
 	if (!dev)
 		goto out;
 
+	eh = kmalloc(sizeof(struct etherh_priv), GFP_KERNEL);
+	if (!eh)
+		goto out_nopriv;
+
 	SET_MODULE_OWNER(dev);
 
-	dev->open	= etherh_open;
-	dev->stop	= etherh_close;
-	dev->set_config	= etherh_set_config;
-	dev->irq	= ec->irq;
-	dev->base_addr	= ecard_address(ec, ECARD_MEMC, 0);
-	dev->mem_end	= ec->cid.product;
+	dev->open		= etherh_open;
+	dev->stop		= etherh_close;
+	dev->set_mac_address	= etherh_set_mac_address;
+	dev->set_config		= etherh_set_config;
+	dev->irq		= ec->irq;
+	dev->base_addr		= ecard_address(ec, ECARD_MEMC, 0);
+	dev->rmem_start		= (unsigned long)eh;
+
+	/*
+	 * IRQ and control port handling
+	 */
 	ec->ops		= &etherh_ops;
+	ec->irq_data	= eh;
+	eh->ctrl	= 0;
+	eh->id		= ec->cid.product;
 
 	switch (ec->cid.product) {
 	case PROD_ANT_ETHERM:
-		if (etherm_addr(dev->dev_addr))
-			goto free;
+		etherm_addr(dev->dev_addr);
 		dev->base_addr += ETHERM_NS8390;
 		dev->mem_start  = dev->base_addr + ETHERM_DATAPORT;
-		ec->irq_data    = (void *)(dev->base_addr + ETHERM_CTRLPORT);
+		eh->ctrl_port   = dev->base_addr + ETHERM_CTRLPORT;
 		break;
 
 	case PROD_I3_ETHERLAN500:
-		if (etherh_addr(dev->dev_addr, ec))
-			goto free;
+		etherh_addr(dev->dev_addr, ec);
 		dev->base_addr += ETHERH500_NS8390;
 		dev->mem_start  = dev->base_addr + ETHERH500_DATAPORT;
-		dev->rmem_start = (unsigned long)
-		ec->irq_data    = (void *)ecard_address (ec, ECARD_IOC, ECARD_FAST)
+		eh->ctrl_port   = ecard_address (ec, ECARD_IOC, ECARD_FAST)
 				  + ETHERH500_CTRLPORT;
 		break;
 
 	case PROD_I3_ETHERLAN600:
 	case PROD_I3_ETHERLAN600A:
-		if (etherh_addr(dev->dev_addr, ec))
-			goto free;
+		etherh_addr(dev->dev_addr, ec);
 		dev->base_addr += ETHERH600_NS8390;
-		dev->mem_start = dev->base_addr + ETHERH600_DATAPORT;
-		ec->irq_data   = (void *)(dev->base_addr + ETHERH600_CTRLPORT);
+		dev->mem_start  = dev->base_addr + ETHERH600_DATAPORT;
+		eh->ctrl_port   = dev->base_addr + ETHERH600_CTRLPORT;
 		break;
 
 	default:
@@ -572,6 +638,12 @@
 		goto release;
 
 	/*
+	 * If we're in the NIC slot, make sure the IRQ is enabled
+	 */
+	if (dev->irq == 11)
+		etherh_set_ctrl(eh, ETHERH_CP_IE);
+
+	/*
 	 * Unfortunately, ethdev_init eventually calls
 	 * ether_setup, which re-writes dev->flags.
 	 */
@@ -636,6 +708,8 @@
 release:
 	release_region(dev->base_addr, 16);
 free:
+	kfree(eh);
+out_nopriv:
 	unregister_netdev(dev);
 	kfree(dev);
 out:
@@ -696,6 +770,7 @@
 		}
 		if (e_card[i]) {
 			e_card[i]->ops = NULL;
+			kfree(e_card[i]->irq_data);
 			ecard_release(e_card[i]);
 			e_card[i] = NULL;
 		}

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)