Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 09dcd604 authored by Jaccon Bastiaansen's avatar Jaccon Bastiaansen Committed by David S. Miller
Browse files

CS89x0 : Use ioread16/iowrite16 on all platforms



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: default avatarJaccon Bastiaansen <jaccon.bastiaansen@gmail.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent a508da6c
Loading
Loading
Loading
Loading
+183 −158
Original line number Diff line number Diff line
@@ -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,9 @@ 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 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);
@@ -267,7 +262,8 @@ static void release_dma_buff(struct net_local *lp);
/*
 * Permit 'cs89x0_dma=N' in the kernel boot environment
 */
#if !defined(MODULE) && (ALLOW_DMA != 0)
#if !defined(MODULE)
#if ALLOW_DMA
static int g_cs89x0_dma;

static int __init dma_fn(char *str)
@@ -277,9 +273,8 @@ static int __init dma_fn(char *str)
}

__setup("cs89x0_dma=", dma_fn);
#endif	/* !defined(MODULE) && (ALLOW_DMA != 0) */
#endif	/* ALLOW_DMA */

#ifndef MODULE
static int g_cs89x0_media__force;

static int __init media_fn(char *str)
@@ -291,58 +286,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.
   If dev->base_addr == 1, always return failure.
   If dev->base_addr == 2, allocate space for the device and return success
   (detachable devices only).
   Return 0 on success.
   */

struct net_device * __init cs89x0_probe(int unit)
{
	struct net_device *dev = alloc_etherdev(sizeof(struct net_local));
	unsigned *port;
	int err = 0;
	int irq;
	int io;

	if (!dev)
		return ERR_PTR(-ENODEV);

	sprintf(dev->name, "eth%d", unit);
	netdev_boot_setup_check(dev);
	io = dev->base_addr;
	irq = dev->irq;

	if (net_debug)
		printk("cs89x0:cs89x0_probe(0x%x)\n", io);

	if (io > 0x1ff)	{	/* Check a single specified location. */
		err = cs89x0_probe1(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)
				break;
			dev->irq = irq;
		}
		if (!*port)
			err = -ENODEV;
	}
	if (err)
		goto out;
	return dev;
out:
	free_netdev(dev);
	printk(KERN_WARNING "cs89x0: no cs8900 or cs8920 detected.  Be sure to disable PnP with SETUP\n");
	return ERR_PTR(err);
}
#endif
#endif

#if defined(CONFIG_MACH_IXDP2351)
@@ -369,36 +312,22 @@ 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)
static void readwords(struct net_local *lp, int portno, void *buf, int length)
{
	u8 *buf8 = (u8 *)buf;

	do {
		u16 tmp16;

		tmp16 = readword(base_addr, portno);
		tmp16 = ioread16(lp->virt_addr + portno);
		*buf8++ = (u8)tmp16;
		*buf8++ = (u8)(tmp16 >> 8);
	} while (--length);
}

static void
writewords(unsigned long base_addr, int portno, void *buf, int length)
static void writewords(struct net_local *lp, int portno, void *buf, int length)
{
	u8 *buf8 = (u8 *)buf;

@@ -407,22 +336,26 @@ writewords(unsigned long base_addr, int portno, void *buf, int length)

		tmp16 = *buf8++;
		tmp16 |= (*buf8++) << 8;
		writeword(base_addr, portno, tmp16);
		iowrite16(tmp16, lp->virt_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 +438,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 +462,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 +495,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,16 +692,125 @@ 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;
}

#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;
}

#ifndef MODULE
/* Check for a network adaptor of this type, and return '0' iff one exists.
   If dev->base_addr == 0, probe all likely locations.
   If dev->base_addr == 1, always return failure.
   If dev->base_addr == 2, allocate space for the device and return success
   (detachable devices only).
   Return 0 on success.
   */

struct net_device * __init cs89x0_probe(int unit)
{
	struct net_device *dev = alloc_etherdev(sizeof(struct net_local));
	unsigned *port;
	int err = 0;
	int irq;
	int io;

	if (!dev)
		return ERR_PTR(-ENODEV);

	sprintf(dev->name, "eth%d", unit);
	netdev_boot_setup_check(dev);
	io = dev->base_addr;
	irq = dev->irq;

	if (net_debug)
		printk(KERN_INFO "cs89x0:cs89x0_probe(0x%x)\n", io);

	if (io > 0x1ff)	{	/* Check a single specified location. */
		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_ioport_probe(dev, *port, 0) == 0)
				break;
			dev->irq = irq;
		}
		if (!*port)
			err = -ENODEV;
	}
	if (err)
		goto out;
	return dev;
out:
	free_netdev(dev);
	printk(KERN_WARNING "cs89x0: no cs8900 or cs8920 detected.  Be sure to disable PnP with SETUP\n");
	return ERR_PTR(err);
}
#endif
#endif

/*********************************
 * This page contains DMA routines
@@ -956,7 +970,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 +981,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);
		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);

		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_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 +1107,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 +1119,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 +1130,7 @@ 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, 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 +1474,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 +1489,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, 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 +1515,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 +1527,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 +1623,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 +1646,9 @@ 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, 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 +1901,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 +1915,11 @@ int __init init_module(void)
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 +1931,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 +1947,21 @@ static int __init cs89x0_platform_probe(struct platform_device *pdev)
		goto free;
	}

	lp->phys_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(mem_res->start, 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(mem_res->start, 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 +1971,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(mem_res->start, lp->size);
free:
	free_netdev(dev);
	return err;
@@ -1965,10 +1983,17 @@ static int cs89x0_platform_remove(struct platform_device *pdev)
{
	struct net_device *dev = platform_get_drvdata(pdev);
	struct net_local *lp = netdev_priv(dev);
	struct resource *mem_res;

	/*
	 * This platform_get_resource() call will not return NULL, because
	 * the same call in cs89x0_platform_probe() has returned a non NULL
	 * value.
	 */
	mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	unregister_netdev(dev);
	iounmap(lp->virt_addr);
	release_mem_region(lp->phys_addr, lp->size);
	release_mem_region(mem_res->start, lp->size);
	free_netdev(dev);
	return 0;
}