From 09dcd604aea065b121c635e40baf4ed2ce60e3f0 Mon Sep 17 00:00:00 2001 From: Jaccon Bastiaansen Date: Thu, 17 May 2012 07:11:42 +0000 Subject: 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: Jaccon Bastiaansen Signed-off-by: David S. Miller --- drivers/net/ethernet/cirrus/cs89x0.c | 341 +++++++++++++++++++---------------- 1 file changed, 183 insertions(+), 158 deletions(-) (limited to 'drivers/net/ethernet/cirrus') diff --git a/drivers/net/ethernet/cirrus/cs89x0.c b/drivers/net/ethernet/cirrus/cs89x0.c index b9406cbfc180..1d59030e7d94 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,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); - - 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 +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 @@ 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 +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; } -- cgit v1.2.3