[PATCH V2] CS89x0 : Use ioread16/iowrite16 on all platforms

Jaccon Bastiaansen jaccon.bastiaansen at gmail.com
Tue May 15 16:15:06 EDT 2012


The use of the inw/outw functions by the cs89x0 platform driver
results in NULL pointer references on ARM platforms and
platforms that do not provide ISA-style programmed I/O accessors.

Using inw/outw also accesses the wrong address space on platforms
that have a PCI I/O space that is not identity-mapped into the
physical address space.

Signed-off-by: Jaccon Bastiaansen <jaccon.bastiaansen at gmail.com>
---
 drivers/net/ethernet/cirrus/cs89x0.c |  238 +++++++++++++++++++---------------
 1 files changed, 135 insertions(+), 103 deletions(-)

diff --git a/drivers/net/ethernet/cirrus/cs89x0.c b/drivers/net/ethernet/cirrus/cs89x0.c
index b9406cb..8081ad5 100644
--- a/drivers/net/ethernet/cirrus/cs89x0.c
+++ b/drivers/net/ethernet/cirrus/cs89x0.c
@@ -222,6 +222,8 @@ struct net_local {
 	int send_underrun;	/* keep track of how many underruns in a row we get */
 	int force;		/* force various values; see FORCE* above. */
 	spinlock_t lock;
+	void __iomem *virt_addr;/* CS89x0 virtual address. */
+	unsigned long size;	/* Length of CS89x0 memory region. */
 #if ALLOW_DMA
 	int use_dma;		/* Flag: we're using dma */
 	int dma;		/* DMA channel */
@@ -230,16 +232,13 @@ struct net_local {
 	unsigned char *end_dma_buff;	/* points to the end of the buffer */
 	unsigned char *rx_dma_ptr;	/* points to the next packet  */
 #endif
-#ifdef CONFIG_CS89x0_PLATFORM
-	void __iomem *virt_addr;/* Virtual address for accessing the CS89x0. */
-	unsigned long phys_addr;/* Physical address for accessing the CS89x0. */
-	unsigned long size;	/* Length of CS89x0 memory region. */
-#endif
 };
 
 /* Index to functions, as function prototypes. */
 
-static int cs89x0_probe1(struct net_device *dev, unsigned long ioaddr, int modular);
+static int cs89x0_probe1(struct net_device *dev,
+			 void __iomem *ioaddr,
+			 int modular);
 static int net_open(struct net_device *dev);
 static netdev_tx_t net_send_packet(struct sk_buff *skb, struct net_device *dev);
 static irqreturn_t net_interrupt(int irq, void *dev_id);
@@ -279,6 +278,67 @@ static int __init dma_fn(char *str)
 __setup("cs89x0_dma=", dma_fn);
 #endif	/* !defined(MODULE) && (ALLOW_DMA != 0) */
 
+#ifndef CONFIG_CS89x0_PLATFORM
+/*
+ * This function converts the I/O port addres used by the cs89x0_probe() and
+ * init_module() functions to the I/O memory address used by the
+ * cs89x0_probe1() function.
+ */
+static int __init
+cs89x0_ioport_probe(struct net_device *dev, unsigned long ioport, int modular)
+{
+	struct net_local *lp = netdev_priv(dev);
+	int ret;
+	void __iomem *io_mem;
+
+	if (!lp)
+		return -ENOMEM;
+
+	dev->base_addr = ioport;
+
+	if (!request_region(ioport, NETCARD_IO_EXTENT, DRV_NAME)) {
+		ret = -EBUSY;
+		goto out;
+	}
+
+	io_mem = ioport_map(ioport & ~3, NETCARD_IO_EXTENT);
+	if (!io_mem) {
+		ret = -ENOMEM;
+		goto release;
+	}
+
+	/* if they give us an odd I/O address, then do ONE write to
+	   the address port, to get it back to address zero, where we
+	   expect to find the EISA signature word. An IO with a base of 0x3
+	   will skip the test for the ADD_PORT. */
+	if (ioport & 1) {
+		if (net_debug > 1)
+			printk(KERN_INFO "%s: odd ioaddr 0x%lx\n",
+			       dev->name,
+			       ioport);
+		if ((ioport & 2) != 2)
+			if ((ioread16(io_mem + ADD_PORT) & ADD_MASK) !=
+			    ADD_SIG) {
+				printk(KERN_ERR "%s: bad signature 0x%x\n",
+					dev->name,
+					ioread16(io_mem + ADD_PORT));
+				ret = -ENODEV;
+				goto unmap;
+			}
+	}
+
+	ret = cs89x0_probe1(dev, io_mem, modular);
+	if (!ret)
+		goto out;
+unmap:
+	ioport_unmap(io_mem);
+release:
+	release_region(ioport, NETCARD_IO_EXTENT);
+out:
+	return ret;
+}
+#endif
+
 #ifndef MODULE
 static int g_cs89x0_media__force;
 
@@ -292,7 +352,6 @@ static int __init media_fn(char *str)
 
 __setup("cs89x0_media=", media_fn);
 
-
 #ifndef CONFIG_CS89x0_PLATFORM
 /* Check for a network adaptor of this type, and return '0' iff one exists.
    If dev->base_addr == 0, probe all likely locations.
@@ -322,12 +381,12 @@ struct net_device * __init cs89x0_probe(int unit)
 		printk("cs89x0:cs89x0_probe(0x%x)\n", io);
 
 	if (io > 0x1ff)	{	/* Check a single specified location. */
-		err = cs89x0_probe1(dev, io, 0);
+		err = cs89x0_ioport_probe(dev, io, 0);
 	} else if (io != 0) {	/* Don't probe at all. */
 		err = -ENXIO;
 	} else {
 		for (port = netcard_portlist; *port; port++) {
-			if (cs89x0_probe1(dev, *port, 0) == 0)
+			if (cs89x0_ioport_probe(dev, *port, 0) == 0)
 				break;
 			dev->irq = irq;
 		}
@@ -369,36 +428,24 @@ writeword(unsigned long base_addr, int portno, u16 value)
 {
 	__raw_writel(value, base_addr + (portno << 1));
 }
-#else
-static u16
-readword(unsigned long base_addr, int portno)
-{
-	return inw(base_addr + portno);
-}
-
-static void
-writeword(unsigned long base_addr, int portno, u16 value)
-{
-	outw(value, base_addr + portno);
-}
 #endif
 
 static void
-readwords(unsigned long base_addr, int portno, void *buf, int length)
+readwords(void __iomem *base_addr, int portno, void *buf, int length)
 {
 	u8 *buf8 = (u8 *)buf;
 
 	do {
 		u16 tmp16;
 
-		tmp16 = readword(base_addr, portno);
+		tmp16 = ioread16(base_addr + portno);
 		*buf8++ = (u8)tmp16;
 		*buf8++ = (u8)(tmp16 >> 8);
 	} while (--length);
 }
 
 static void
-writewords(unsigned long base_addr, int portno, void *buf, int length)
+writewords(void __iomem *base_addr, int portno, void *buf, int length)
 {
 	u8 *buf8 = (u8 *)buf;
 
@@ -407,22 +454,26 @@ writewords(unsigned long base_addr, int portno, void *buf, int length)
 
 		tmp16 = *buf8++;
 		tmp16 |= (*buf8++) << 8;
-		writeword(base_addr, portno, tmp16);
+		iowrite16(tmp16, base_addr + portno);
 	} while (--length);
 }
 
 static u16
 readreg(struct net_device *dev, u16 regno)
 {
-	writeword(dev->base_addr, ADD_PORT, regno);
-	return readword(dev->base_addr, DATA_PORT);
+	struct net_local *lp = netdev_priv(dev);
+
+	iowrite16(regno, lp->virt_addr + ADD_PORT);
+	return ioread16(lp->virt_addr + DATA_PORT);
 }
 
 static void
 writereg(struct net_device *dev, u16 regno, u16 value)
 {
-	writeword(dev->base_addr, ADD_PORT, regno);
-	writeword(dev->base_addr, DATA_PORT, value);
+	struct net_local *lp = netdev_priv(dev);
+
+	iowrite16(regno, lp->virt_addr + ADD_PORT);
+	iowrite16(value, lp->virt_addr + DATA_PORT);
 }
 
 static int __init
@@ -505,7 +556,7 @@ static const struct net_device_ops net_ops = {
  */
 
 static int __init
-cs89x0_probe1(struct net_device *dev, unsigned long ioaddr, int modular)
+cs89x0_probe1(struct net_device *dev, void __iomem *ioaddr, int modular)
 {
 	struct net_local *lp = netdev_priv(dev);
 	static unsigned version_printed;
@@ -529,50 +580,22 @@ cs89x0_probe1(struct net_device *dev, unsigned long ioaddr, int modular)
 #endif
 		lp->force = g_cs89x0_media__force;
 #endif
-
         }
 
-	/* Grab the region so we can find another board if autoIRQ fails. */
-	/* WTF is going on here? */
-	if (!request_region(ioaddr & ~3, NETCARD_IO_EXTENT, DRV_NAME)) {
-		printk(KERN_ERR "%s: request_region(0x%lx, 0x%x) failed\n",
-				DRV_NAME, ioaddr, NETCARD_IO_EXTENT);
-		retval = -EBUSY;
-		goto out1;
-	}
+	printk(KERN_DEBUG "PP_addr at %p[%x]: 0x%x\n",
+			ioaddr, ADD_PORT, ioread16(ioaddr + ADD_PORT));
+	iowrite16(PP_ChipID, ioaddr + ADD_PORT);
 
-	/* if they give us an odd I/O address, then do ONE write to
-           the address port, to get it back to address zero, where we
-           expect to find the EISA signature word. An IO with a base of 0x3
-	   will skip the test for the ADD_PORT. */
-	if (ioaddr & 1) {
-		if (net_debug > 1)
-			printk(KERN_INFO "%s: odd ioaddr 0x%lx\n", dev->name, ioaddr);
-	        if ((ioaddr & 2) != 2)
-	        	if ((readword(ioaddr & ~3, ADD_PORT) & ADD_MASK) != ADD_SIG) {
-				printk(KERN_ERR "%s: bad signature 0x%x\n",
-					dev->name, readword(ioaddr & ~3, ADD_PORT));
-		        	retval = -ENODEV;
-				goto out2;
-			}
-	}
-
-	ioaddr &= ~3;
-	printk(KERN_DEBUG "PP_addr at %lx[%x]: 0x%x\n",
-			ioaddr, ADD_PORT, readword(ioaddr, ADD_PORT));
-	writeword(ioaddr, ADD_PORT, PP_ChipID);
-
-	tmp = readword(ioaddr, DATA_PORT);
+	tmp = ioread16(ioaddr + DATA_PORT);
 	if (tmp != CHIP_EISA_ID_SIG) {
-		printk(KERN_DEBUG "%s: incorrect signature at %lx[%x]: 0x%x!="
+		printk(KERN_DEBUG "%s: incorrect signature at %p[%x]: 0x%x!="
 			CHIP_EISA_ID_SIG_STR "\n",
 			dev->name, ioaddr, DATA_PORT, tmp);
   		retval = -ENODEV;
-  		goto out2;
+		goto out1;
 	}
 
-	/* Fill in the 'dev' fields. */
-	dev->base_addr = ioaddr;
+	lp->virt_addr = ioaddr;
 
 	/* get the chip type */
 	rev_type = readreg(dev, PRODUCT_ID_ADD);
@@ -590,12 +613,12 @@ cs89x0_probe1(struct net_device *dev, unsigned long ioaddr, int modular)
 	if (net_debug  &&  version_printed++ == 0)
 		printk(version);
 
-	printk(KERN_INFO "%s: cs89%c0%s rev %c found at %#3lx ",
+	printk(KERN_INFO "%s: cs89%c0%s rev %c found at %p ",
 	       dev->name,
 	       lp->chip_type==CS8900?'0':'2',
 	       lp->chip_type==CS8920M?"M":"",
 	       lp->chip_revision,
-	       dev->base_addr);
+	       lp->virt_addr);
 
 	reset_chip(dev);
 
@@ -787,12 +810,10 @@ cs89x0_probe1(struct net_device *dev, unsigned long ioaddr, int modular)
 
 	retval = register_netdev(dev);
 	if (retval)
-		goto out3;
+		goto out2;
 	return 0;
-out3:
-	writeword(dev->base_addr, ADD_PORT, PP_ChipID);
 out2:
-	release_region(ioaddr & ~3, NETCARD_IO_EXTENT);
+	iowrite16(PP_ChipID, lp->virt_addr + ADD_PORT);
 out1:
 	return retval;
 }
@@ -956,7 +977,6 @@ static void __init reset_chip(struct net_device *dev)
 #if !defined(CONFIG_MACH_MX31ADS)
 #if !defined(CS89x0_NONISA_IRQ)
 	struct net_local *lp = netdev_priv(dev);
-	int ioaddr = dev->base_addr;
 #endif /* CS89x0_NONISA_IRQ */
 	int reset_start_time;
 
@@ -968,13 +988,15 @@ static void __init reset_chip(struct net_device *dev)
 #if !defined(CS89x0_NONISA_IRQ)
 	if (lp->chip_type != CS8900) {
 		/* Hardware problem requires PNP registers to be reconfigured after a reset */
-		writeword(ioaddr, ADD_PORT, PP_CS8920_ISAINT);
-		outb(dev->irq, ioaddr + DATA_PORT);
-		outb(0,      ioaddr + DATA_PORT + 1);
-
-		writeword(ioaddr, ADD_PORT, PP_CS8920_ISAMemB);
-		outb((dev->mem_start >> 16) & 0xff, ioaddr + DATA_PORT);
-		outb((dev->mem_start >> 8) & 0xff,   ioaddr + DATA_PORT + 1);
+		iowrite16(PP_CS8920_ISAINT, lp->virt_addr + ADD_PORT);
+		iowrite8(dev->irq, lp->virt_addr + DATA_PORT);
+		iowrite8(0, lp->virt_addr + DATA_PORT + 1);
+
+		iowrite16(PP_CS8920_ISAMemB, lp->virt_addr + ADD_PORT);
+		iowrite8((dev->mem_start >> 16) & 0xff,
+			 lp->virt_addr + DATA_PORT);
+		iowrite8((dev->mem_start >> 8) & 0xff,
+			 lp->virt_addr + DATA_PORT + 1);
 	}
 #endif /* CS89x0_NONISA_IRQ */
 
@@ -1092,6 +1114,7 @@ detect_tp(struct net_device *dev)
 static int
 send_test_pkt(struct net_device *dev)
 {
+	struct net_local *lp = netdev_priv(dev);
 	char test_packet[] = { 0,0,0,0,0,0, 0,0,0,0,0,0,
 				 0, 46, /* A 46 in network order */
 				 0, 0, /* DSAP=0 & SSAP=0 fields */
@@ -1103,8 +1126,8 @@ send_test_pkt(struct net_device *dev)
 	memcpy(test_packet,          dev->dev_addr, ETH_ALEN);
 	memcpy(test_packet+ETH_ALEN, dev->dev_addr, ETH_ALEN);
 
-        writeword(dev->base_addr, TX_CMD_PORT, TX_AFTER_ALL);
-        writeword(dev->base_addr, TX_LEN_PORT, ETH_ZLEN);
+	iowrite16(TX_AFTER_ALL, lp->virt_addr + TX_CMD_PORT);
+	iowrite16(ETH_ZLEN, lp->virt_addr + TX_LEN_PORT);
 
 	/* Test to see if the chip has allocated memory for the packet */
 	while (jiffies - timenow < 5)
@@ -1114,7 +1137,10 @@ send_test_pkt(struct net_device *dev)
 		return 0;	/* this shouldn't happen */
 
 	/* Write the contents of the packet */
-	writewords(dev->base_addr, TX_FRAME_PORT,test_packet,(ETH_ZLEN+1) >>1);
+	writewords(lp->virt_addr,
+		   TX_FRAME_PORT,
+		   test_packet,
+		   (ETH_ZLEN+1) >> 1);
 
 	if (net_debug > 1) printk("Sending test packet ");
 	/* wait a couple of jiffies for packet to be received */
@@ -1458,8 +1484,8 @@ static netdev_tx_t net_send_packet(struct sk_buff *skb,struct net_device *dev)
 	netif_stop_queue(dev);
 
 	/* initiate a transmit sequence */
-	writeword(dev->base_addr, TX_CMD_PORT, lp->send_cmd);
-	writeword(dev->base_addr, TX_LEN_PORT, skb->len);
+	iowrite16(lp->send_cmd, lp->virt_addr + TX_CMD_PORT);
+	iowrite16(skb->len, lp->virt_addr + TX_LEN_PORT);
 
 	/* Test to see if the chip has allocated memory for the packet */
 	if ((readreg(dev, PP_BusST) & READY_FOR_TX_NOW) == 0) {
@@ -1473,7 +1499,7 @@ static netdev_tx_t net_send_packet(struct sk_buff *skb,struct net_device *dev)
 		return NETDEV_TX_BUSY;
 	}
 	/* Write the contents of the packet */
-	writewords(dev->base_addr, TX_FRAME_PORT,skb->data,(skb->len+1) >>1);
+	writewords(lp->virt_addr, TX_FRAME_PORT, skb->data, (skb->len+1) >> 1);
 	spin_unlock_irqrestore(&lp->lock, flags);
 	dev->stats.tx_bytes += skb->len;
 	dev_kfree_skb (skb);
@@ -1499,10 +1525,9 @@ static irqreturn_t net_interrupt(int irq, void *dev_id)
 {
 	struct net_device *dev = dev_id;
 	struct net_local *lp;
-	int ioaddr, status;
+	int status;
  	int handled = 0;
 
-	ioaddr = dev->base_addr;
 	lp = netdev_priv(dev);
 
 	/* we MUST read all the events out of the ISQ, otherwise we'll never
@@ -1512,7 +1537,7 @@ static irqreturn_t net_interrupt(int irq, void *dev_id)
            course, if you're on a slow machine, and packets are arriving
            faster than you can read them off, you're screwed.  Hasta la
            vista, baby!  */
-	while ((status = readword(dev->base_addr, ISQ_PORT))) {
+	while ((status = ioread16(lp->virt_addr + ISQ_PORT))) {
 		if (net_debug > 4)printk("%s: event=%04x\n", dev->name, status);
 		handled = 1;
 		switch(status & ISQ_EVENT_MASK) {
@@ -1608,12 +1633,12 @@ count_rx_errors(int status, struct net_device *dev)
 static void
 net_rx(struct net_device *dev)
 {
+	struct net_local *lp = netdev_priv(dev);
 	struct sk_buff *skb;
 	int status, length;
 
-	int ioaddr = dev->base_addr;
-	status = readword(ioaddr, RX_FRAME_PORT);
-	length = readword(ioaddr, RX_FRAME_PORT);
+	status = ioread16(lp->virt_addr + RX_FRAME_PORT);
+	length = ioread16(lp->virt_addr + RX_FRAME_PORT);
 
 	if ((status & RX_OK) == 0) {
 		count_rx_errors(status, dev);
@@ -1631,9 +1656,12 @@ net_rx(struct net_device *dev)
 	}
 	skb_reserve(skb, 2);	/* longword align L3 header */
 
-	readwords(ioaddr, RX_FRAME_PORT, skb_put(skb, length), length >> 1);
+	readwords(lp->virt_addr,
+		  RX_FRAME_PORT,
+		  skb_put(skb, length),
+		  length >> 1);
 	if (length & 1)
-		skb->data[length-1] = readword(ioaddr, RX_FRAME_PORT);
+		skb->data[length-1] = ioread16(lp->virt_addr + RX_FRAME_PORT);
 
 	if (net_debug > 3) {
 		printk(	"%s: received %d byte packet of type %x\n",
@@ -1886,7 +1914,7 @@ int __init init_module(void)
 		goto out;
 	}
 #endif
-	ret = cs89x0_probe1(dev, io, 1);
+	ret = cs89x0_ioport_probe(dev, io, 1);
 	if (ret)
 		goto out;
 
@@ -1900,8 +1928,11 @@ out:
 void __exit
 cleanup_module(void)
 {
+	struct net_local *lp = netdev_priv(dev_cs89x0);
+
 	unregister_netdev(dev_cs89x0);
-	writeword(dev_cs89x0->base_addr, ADD_PORT, PP_ChipID);
+	iowrite16(PP_ChipID, lp->virt_addr + ADD_PORT);
+	ioport_unmap(lp->virt_addr);
 	release_region(dev_cs89x0->base_addr, NETCARD_IO_EXTENT);
 	free_netdev(dev_cs89x0);
 }
@@ -1913,6 +1944,7 @@ static int __init cs89x0_platform_probe(struct platform_device *pdev)
 	struct net_device *dev = alloc_etherdev(sizeof(struct net_local));
 	struct net_local *lp;
 	struct resource *mem_res;
+	void __iomem *virt_addr;
 	int err;
 
 	if (!dev)
@@ -1928,22 +1960,22 @@ static int __init cs89x0_platform_probe(struct platform_device *pdev)
 		goto free;
 	}
 
-	lp->phys_addr = mem_res->start;
+	dev->base_addr = mem_res->start;
 	lp->size = resource_size(mem_res);
-	if (!request_mem_region(lp->phys_addr, lp->size, DRV_NAME)) {
+	if (!request_mem_region(dev->base_addr, lp->size, DRV_NAME)) {
 		dev_warn(&dev->dev, "request_mem_region() failed.\n");
 		err = -EBUSY;
 		goto free;
 	}
 
-	lp->virt_addr = ioremap(lp->phys_addr, lp->size);
-	if (!lp->virt_addr) {
+	virt_addr = ioremap(dev->base_addr, lp->size);
+	if (!virt_addr) {
 		dev_warn(&dev->dev, "ioremap() failed.\n");
 		err = -ENOMEM;
 		goto release;
 	}
 
-	err = cs89x0_probe1(dev, (unsigned long)lp->virt_addr, 0);
+	err = cs89x0_probe1(dev, virt_addr, 0);
 	if (err) {
 		dev_warn(&dev->dev, "no cs8900 or cs8920 detected.\n");
 		goto unmap;
@@ -1953,9 +1985,9 @@ static int __init cs89x0_platform_probe(struct platform_device *pdev)
 	return 0;
 
 unmap:
-	iounmap(lp->virt_addr);
+	iounmap(virt_addr);
 release:
-	release_mem_region(lp->phys_addr, lp->size);
+	release_mem_region(dev->base_addr, lp->size);
 free:
 	free_netdev(dev);
 	return err;
@@ -1968,7 +2000,7 @@ static int cs89x0_platform_remove(struct platform_device *pdev)
 
 	unregister_netdev(dev);
 	iounmap(lp->virt_addr);
-	release_mem_region(lp->phys_addr, lp->size);
+	release_mem_region(dev->base_addr, lp->size);
 	free_netdev(dev);
 	return 0;
 }
-- 
1.7.1




More information about the linux-arm-kernel mailing list