![Tejun Heo](/assets/img/avatar_default.png)
percpu.h is included by sched.h and module.h and thus ends up being included when building most .c files. percpu.h includes slab.h which in turn includes gfp.h making everything defined by the two files universally available and complicating inclusion dependencies. percpu.h -> slab.h dependency is about to be removed. Prepare for this change by updating users of gfp and slab facilities include those headers directly instead of assuming availability. As this conversion needs to touch large number of source files, the following script is used as the basis of conversion. http://userweb.kernel.org/~tj/misc/slabh-sweep.py The script does the followings. * Scan files for gfp and slab usages and update includes such that only the necessary includes are there. ie. if only gfp is used, gfp.h, if slab is used, slab.h. * When the script inserts a new include, it looks at the include blocks and try to put the new include such that its order conforms to its surrounding. It's put in the include block which contains core kernel includes, in the same order that the rest are ordered - alphabetical, Christmas tree, rev-Xmas-tree or at the end if there doesn't seem to be any matching order. * If the script can't find a place to put a new include (mostly because the file doesn't have fitting include block), it prints out an error message indicating which .h file needs to be added to the file. The conversion was done in the following steps. 1. The initial automatic conversion of all .c files updated slightly over 4000 files, deleting around 700 includes and adding ~480 gfp.h and ~3000 slab.h inclusions. The script emitted errors for ~400 files. 2. Each error was manually checked. Some didn't need the inclusion, some needed manual addition while adding it to implementation .h or embedding .c file was more appropriate for others. This step added inclusions to around 150 files. 3. The script was run again and the output was compared to the edits from #2 to make sure no file was left behind. 4. Several build tests were done and a couple of problems were fixed. e.g. lib/decompress_*.c used malloc/free() wrappers around slab APIs requiring slab.h to be added manually. 5. The script was run on all .h files but without automatically editing them as sprinkling gfp.h and slab.h inclusions around .h files could easily lead to inclusion dependency hell. Most gfp.h inclusion directives were ignored as stuff from gfp.h was usually wildly available and often used in preprocessor macros. Each slab.h inclusion directive was examined and added manually as necessary. 6. percpu.h was updated not to include slab.h. 7. Build test were done on the following configurations and failures were fixed. CONFIG_GCOV_KERNEL was turned off for all tests (as my distributed build env didn't work with gcov compiles) and a few more options had to be turned off depending on archs to make things build (like ipr on powerpc/64 which failed due to missing writeq). * x86 and x86_64 UP and SMP allmodconfig and a custom test config. * powerpc and powerpc64 SMP allmodconfig * sparc and sparc64 SMP allmodconfig * ia64 SMP allmodconfig * s390 SMP allmodconfig * alpha SMP allmodconfig * um on x86_64 SMP allmodconfig 8. percpu.h modifications were reverted so that it could be applied as a separate patch and serve as bisection point. Given the fact that I had only a couple of failures from tests on step 6, I'm fairly confident about the coverage of this conversion patch. If there is a breakage, it's likely to be something in one of the arch headers which should be easily discoverable easily on most builds of the specific arch. Signed-off-by: Tejun Heo <tj@kernel.org> Guess-its-ok-by: Christoph Lameter <cl@linux-foundation.org> Cc: Ingo Molnar <mingo@redhat.com> Cc: Lee Schermerhorn <Lee.Schermerhorn@hp.com>
866 lines
20 KiB
C
866 lines
20 KiB
C
/*
|
|
* linux/drivers/acorn/net/etherh.c
|
|
*
|
|
* 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
|
|
* published by the Free Software Foundation.
|
|
*
|
|
* NS8390 I-cubed EtherH and ANT EtherM specific driver
|
|
* Thanks to I-Cubed for information on their cards.
|
|
* EtherM conversion (C) 1999 Chris Kemp and Tim Watterton
|
|
* EtherM integration (C) 2000 Aleph One Ltd (Tak-Shing Chan)
|
|
* EtherM integration re-engineered by Russell King.
|
|
*
|
|
* Changelog:
|
|
* 08-12-1996 RMK 1.00 Created
|
|
* RMK 1.03 Added support for EtherLan500 cards
|
|
* 23-11-1997 RMK 1.04 Added media autodetection
|
|
* 16-04-1998 RMK 1.05 Improved media autodetection
|
|
* 10-02-2000 RMK 1.06 Updated for 2.3.43
|
|
* 13-05-2000 RMK 1.07 Updated for 2.3.99-pre8
|
|
* 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>
|
|
#include <linux/kernel.h>
|
|
#include <linux/types.h>
|
|
#include <linux/fcntl.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/ioport.h>
|
|
#include <linux/in.h>
|
|
#include <linux/string.h>
|
|
#include <linux/errno.h>
|
|
#include <linux/netdevice.h>
|
|
#include <linux/etherdevice.h>
|
|
#include <linux/ethtool.h>
|
|
#include <linux/skbuff.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/device.h>
|
|
#include <linux/init.h>
|
|
#include <linux/bitops.h>
|
|
#include <linux/jiffies.h>
|
|
|
|
#include <asm/system.h>
|
|
#include <asm/ecard.h>
|
|
#include <asm/io.h>
|
|
|
|
#define EI_SHIFT(x) (ei_local->reg_offset[x])
|
|
|
|
#define ei_inb(_p) readb((void __iomem *)_p)
|
|
#define ei_outb(_v,_p) writeb(_v,(void __iomem *)_p)
|
|
#define ei_inb_p(_p) readb((void __iomem *)_p)
|
|
#define ei_outb_p(_v,_p) writeb(_v,(void __iomem *)_p)
|
|
|
|
#define NET_DEBUG 0
|
|
#define DEBUG_INIT 2
|
|
|
|
#define DRV_NAME "etherh"
|
|
#define DRV_VERSION "1.11"
|
|
|
|
static char version[] __initdata =
|
|
"EtherH/EtherM Driver (c) 2002-2004 Russell King " DRV_VERSION "\n";
|
|
|
|
#include "../lib8390.c"
|
|
|
|
static unsigned int net_debug = NET_DEBUG;
|
|
|
|
struct etherh_priv {
|
|
void __iomem *ioc_fast;
|
|
void __iomem *memc;
|
|
void __iomem *dma_base;
|
|
unsigned int id;
|
|
void __iomem *ctrl_port;
|
|
unsigned char ctrl;
|
|
u32 supported;
|
|
};
|
|
|
|
struct etherh_data {
|
|
unsigned long ns8390_offset;
|
|
unsigned long dataport_offset;
|
|
unsigned long ctrlport_offset;
|
|
int ctrl_ioc;
|
|
const char name[16];
|
|
u32 supported;
|
|
unsigned char tx_start_page;
|
|
unsigned char stop_page;
|
|
};
|
|
|
|
MODULE_AUTHOR("Russell King");
|
|
MODULE_DESCRIPTION("EtherH/EtherM driver");
|
|
MODULE_LICENSE("GPL");
|
|
|
|
#define ETHERH500_DATAPORT 0x800 /* MEMC */
|
|
#define ETHERH500_NS8390 0x000 /* MEMC */
|
|
#define ETHERH500_CTRLPORT 0x800 /* IOC */
|
|
|
|
#define ETHERH600_DATAPORT 0x040 /* MEMC */
|
|
#define ETHERH600_NS8390 0x800 /* MEMC */
|
|
#define ETHERH600_CTRLPORT 0x200 /* MEMC */
|
|
|
|
#define ETHERH_CP_IE 1
|
|
#define ETHERH_CP_IF 2
|
|
#define ETHERH_CP_HEARTBEAT 2
|
|
|
|
#define ETHERH_TX_START_PAGE 1
|
|
#define ETHERH_STOP_PAGE 127
|
|
|
|
/*
|
|
* These came from CK/TEW
|
|
*/
|
|
#define ETHERM_DATAPORT 0x200 /* MEMC */
|
|
#define ETHERM_NS8390 0x800 /* MEMC */
|
|
#define ETHERM_CTRLPORT 0x23c /* MEMC */
|
|
|
|
#define ETHERM_TX_START_PAGE 64
|
|
#define ETHERM_STOP_PAGE 127
|
|
|
|
/* ------------------------------------------------------------------------ */
|
|
|
|
#define etherh_priv(dev) \
|
|
((struct etherh_priv *)(((char *)netdev_priv(dev)) + sizeof(struct ei_device)))
|
|
|
|
static inline void etherh_set_ctrl(struct etherh_priv *eh, unsigned char mask)
|
|
{
|
|
unsigned char ctrl = eh->ctrl | mask;
|
|
eh->ctrl = ctrl;
|
|
writeb(ctrl, eh->ctrl_port);
|
|
}
|
|
|
|
static inline void etherh_clr_ctrl(struct etherh_priv *eh, unsigned char mask)
|
|
{
|
|
unsigned char ctrl = eh->ctrl & ~mask;
|
|
eh->ctrl = ctrl;
|
|
writeb(ctrl, eh->ctrl_port);
|
|
}
|
|
|
|
static inline unsigned int etherh_get_stat(struct etherh_priv *eh)
|
|
{
|
|
return readb(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 = netdev_priv(dev);
|
|
unsigned long flags;
|
|
void __iomem *addr;
|
|
|
|
local_irq_save(flags);
|
|
|
|
/* set the interface type */
|
|
switch (etherh_priv(dev)->id) {
|
|
case PROD_I3_ETHERLAN600:
|
|
case PROD_I3_ETHERLAN600A:
|
|
addr = (void __iomem *)dev->base_addr + EN0_RCNTHI;
|
|
|
|
switch (dev->if_port) {
|
|
case IF_PORT_10BASE2:
|
|
writeb((readb(addr) & 0xf8) | 1, addr);
|
|
break;
|
|
case IF_PORT_10BASET:
|
|
writeb((readb(addr) & 0xf8), addr);
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case PROD_I3_ETHERLAN500:
|
|
switch (dev->if_port) {
|
|
case IF_PORT_10BASE2:
|
|
etherh_clr_ctrl(etherh_priv(dev), ETHERH_CP_IF);
|
|
break;
|
|
|
|
case IF_PORT_10BASET:
|
|
etherh_set_ctrl(etherh_priv(dev), ETHERH_CP_IF);
|
|
break;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
local_irq_restore(flags);
|
|
}
|
|
|
|
static int
|
|
etherh_getifstat(struct net_device *dev)
|
|
{
|
|
struct ei_device *ei_local = netdev_priv(dev);
|
|
void __iomem *addr;
|
|
int stat = 0;
|
|
|
|
switch (etherh_priv(dev)->id) {
|
|
case PROD_I3_ETHERLAN600:
|
|
case PROD_I3_ETHERLAN600A:
|
|
addr = (void __iomem *)dev->base_addr + EN0_RCNTHI;
|
|
switch (dev->if_port) {
|
|
case IF_PORT_10BASE2:
|
|
stat = 1;
|
|
break;
|
|
case IF_PORT_10BASET:
|
|
stat = readb(addr) & 4;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case PROD_I3_ETHERLAN500:
|
|
switch (dev->if_port) {
|
|
case IF_PORT_10BASE2:
|
|
stat = 1;
|
|
break;
|
|
case IF_PORT_10BASET:
|
|
stat = etherh_get_stat(etherh_priv(dev)) & ETHERH_CP_HEARTBEAT;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
stat = 0;
|
|
break;
|
|
}
|
|
|
|
return stat != 0;
|
|
}
|
|
|
|
/*
|
|
* Configure the interface. Note that we ignore the other
|
|
* parts of ifmap, since its mostly meaningless for this driver.
|
|
*/
|
|
static int etherh_set_config(struct net_device *dev, struct ifmap *map)
|
|
{
|
|
switch (map->port) {
|
|
case IF_PORT_10BASE2:
|
|
case IF_PORT_10BASET:
|
|
/*
|
|
* If the user explicitly sets the interface
|
|
* media type, turn off automedia detection.
|
|
*/
|
|
dev->flags &= ~IFF_AUTOMEDIA;
|
|
dev->if_port = map->port;
|
|
break;
|
|
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
etherh_setif(dev);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Reset the 8390 (hard reset). Note that we can't actually do this.
|
|
*/
|
|
static void
|
|
etherh_reset(struct net_device *dev)
|
|
{
|
|
struct ei_device *ei_local = netdev_priv(dev);
|
|
void __iomem *addr = (void __iomem *)dev->base_addr;
|
|
|
|
writeb(E8390_NODMA+E8390_PAGE0+E8390_STOP, addr);
|
|
|
|
/*
|
|
* See if we need to change the interface type.
|
|
* Note that we use 'interface_num' as a flag
|
|
* to indicate that we need to change the media.
|
|
*/
|
|
if (dev->flags & IFF_AUTOMEDIA && ei_local->interface_num) {
|
|
ei_local->interface_num = 0;
|
|
|
|
if (dev->if_port == IF_PORT_10BASET)
|
|
dev->if_port = IF_PORT_10BASE2;
|
|
else
|
|
dev->if_port = IF_PORT_10BASET;
|
|
|
|
etherh_setif(dev);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Write a block of data out to the 8390
|
|
*/
|
|
static void
|
|
etherh_block_output (struct net_device *dev, int count, const unsigned char *buf, int start_page)
|
|
{
|
|
struct ei_device *ei_local = netdev_priv(dev);
|
|
unsigned long dma_start;
|
|
void __iomem *dma_base, *addr;
|
|
|
|
if (ei_local->dmaing) {
|
|
printk(KERN_ERR "%s: DMAing conflict in etherh_block_input: "
|
|
" DMAstat %d irqlock %d\n", dev->name,
|
|
ei_local->dmaing, ei_local->irqlock);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* 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 = (void __iomem *)dev->base_addr;
|
|
dma_base = etherh_priv(dev)->dma_base;
|
|
|
|
count = (count + 1) & ~1;
|
|
writeb (E8390_NODMA | E8390_PAGE0 | E8390_START, addr + E8390_CMD);
|
|
|
|
writeb (0x42, addr + EN0_RCNTLO);
|
|
writeb (0x00, addr + EN0_RCNTHI);
|
|
writeb (0x42, addr + EN0_RSARLO);
|
|
writeb (0x00, addr + EN0_RSARHI);
|
|
writeb (E8390_RREAD | E8390_START, addr + E8390_CMD);
|
|
|
|
udelay (1);
|
|
|
|
writeb (ENISR_RDC, addr + EN0_ISR);
|
|
writeb (count, addr + EN0_RCNTLO);
|
|
writeb (count >> 8, addr + EN0_RCNTHI);
|
|
writeb (0, addr + EN0_RSARLO);
|
|
writeb (start_page, addr + EN0_RSARHI);
|
|
writeb (E8390_RWRITE | E8390_START, addr + E8390_CMD);
|
|
|
|
if (ei_local->word16)
|
|
writesw (dma_base, buf, count >> 1);
|
|
else
|
|
writesb (dma_base, buf, count);
|
|
|
|
dma_start = jiffies;
|
|
|
|
while ((readb (addr + EN0_ISR) & ENISR_RDC) == 0)
|
|
if (time_after(jiffies, dma_start + 2*HZ/100)) { /* 20ms */
|
|
printk(KERN_ERR "%s: timeout waiting for TX RDC\n",
|
|
dev->name);
|
|
etherh_reset (dev);
|
|
__NS8390_init (dev, 1);
|
|
break;
|
|
}
|
|
|
|
writeb (ENISR_RDC, addr + EN0_ISR);
|
|
ei_local->dmaing = 0;
|
|
}
|
|
|
|
/*
|
|
* Read a block of data from the 8390
|
|
*/
|
|
static void
|
|
etherh_block_input (struct net_device *dev, int count, struct sk_buff *skb, int ring_offset)
|
|
{
|
|
struct ei_device *ei_local = netdev_priv(dev);
|
|
unsigned char *buf;
|
|
void __iomem *dma_base, *addr;
|
|
|
|
if (ei_local->dmaing) {
|
|
printk(KERN_ERR "%s: DMAing conflict in etherh_block_input: "
|
|
" DMAstat %d irqlock %d\n", dev->name,
|
|
ei_local->dmaing, ei_local->irqlock);
|
|
return;
|
|
}
|
|
|
|
ei_local->dmaing = 1;
|
|
|
|
addr = (void __iomem *)dev->base_addr;
|
|
dma_base = etherh_priv(dev)->dma_base;
|
|
|
|
buf = skb->data;
|
|
writeb (E8390_NODMA | E8390_PAGE0 | E8390_START, addr + E8390_CMD);
|
|
writeb (count, addr + EN0_RCNTLO);
|
|
writeb (count >> 8, addr + EN0_RCNTHI);
|
|
writeb (ring_offset, addr + EN0_RSARLO);
|
|
writeb (ring_offset >> 8, addr + EN0_RSARHI);
|
|
writeb (E8390_RREAD | E8390_START, addr + E8390_CMD);
|
|
|
|
if (ei_local->word16) {
|
|
readsw (dma_base, buf, count >> 1);
|
|
if (count & 1)
|
|
buf[count - 1] = readb (dma_base);
|
|
} else
|
|
readsb (dma_base, buf, count);
|
|
|
|
writeb (ENISR_RDC, addr + EN0_ISR);
|
|
ei_local->dmaing = 0;
|
|
}
|
|
|
|
/*
|
|
* Read a header from the 8390
|
|
*/
|
|
static void
|
|
etherh_get_header (struct net_device *dev, struct e8390_pkt_hdr *hdr, int ring_page)
|
|
{
|
|
struct ei_device *ei_local = netdev_priv(dev);
|
|
void __iomem *dma_base, *addr;
|
|
|
|
if (ei_local->dmaing) {
|
|
printk(KERN_ERR "%s: DMAing conflict in etherh_get_header: "
|
|
" DMAstat %d irqlock %d\n", dev->name,
|
|
ei_local->dmaing, ei_local->irqlock);
|
|
return;
|
|
}
|
|
|
|
ei_local->dmaing = 1;
|
|
|
|
addr = (void __iomem *)dev->base_addr;
|
|
dma_base = etherh_priv(dev)->dma_base;
|
|
|
|
writeb (E8390_NODMA | E8390_PAGE0 | E8390_START, addr + E8390_CMD);
|
|
writeb (sizeof (*hdr), addr + EN0_RCNTLO);
|
|
writeb (0, addr + EN0_RCNTHI);
|
|
writeb (0, addr + EN0_RSARLO);
|
|
writeb (ring_page, addr + EN0_RSARHI);
|
|
writeb (E8390_RREAD | E8390_START, addr + E8390_CMD);
|
|
|
|
if (ei_local->word16)
|
|
readsw (dma_base, hdr, sizeof (*hdr) >> 1);
|
|
else
|
|
readsb (dma_base, hdr, sizeof (*hdr));
|
|
|
|
writeb (ENISR_RDC, addr + EN0_ISR);
|
|
ei_local->dmaing = 0;
|
|
}
|
|
|
|
/*
|
|
* Open/initialize the board. This is called (in the current kernel)
|
|
* sometime after booting when the 'ifconfig' program is run.
|
|
*
|
|
* This routine should set everything up anew at each open, even
|
|
* registers that "should" only need to be set once at boot, so that
|
|
* there is non-reboot way to recover if something goes wrong.
|
|
*/
|
|
static int
|
|
etherh_open(struct net_device *dev)
|
|
{
|
|
struct ei_device *ei_local = netdev_priv(dev);
|
|
|
|
if (!is_valid_ether_addr(dev->dev_addr)) {
|
|
printk(KERN_WARNING "%s: invalid ethernet MAC address\n",
|
|
dev->name);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (request_irq(dev->irq, __ei_interrupt, 0, dev->name, dev))
|
|
return -EAGAIN;
|
|
|
|
/*
|
|
* Make sure that we aren't going to change the
|
|
* media type on the next reset - we are about to
|
|
* do automedia manually now.
|
|
*/
|
|
ei_local->interface_num = 0;
|
|
|
|
/*
|
|
* If we are doing automedia detection, do it now.
|
|
* This is more reliable than the 8390's detection.
|
|
*/
|
|
if (dev->flags & IFF_AUTOMEDIA) {
|
|
dev->if_port = IF_PORT_10BASET;
|
|
etherh_setif(dev);
|
|
mdelay(1);
|
|
if (!etherh_getifstat(dev)) {
|
|
dev->if_port = IF_PORT_10BASE2;
|
|
etherh_setif(dev);
|
|
}
|
|
} else
|
|
etherh_setif(dev);
|
|
|
|
etherh_reset(dev);
|
|
__ei_open(dev);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* The inverse routine to etherh_open().
|
|
*/
|
|
static int
|
|
etherh_close(struct net_device *dev)
|
|
{
|
|
__ei_close (dev);
|
|
free_irq (dev->irq, dev);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Initialisation
|
|
*/
|
|
|
|
static void __init etherh_banner(void)
|
|
{
|
|
static int version_printed;
|
|
|
|
if (net_debug && version_printed++ == 0)
|
|
printk(KERN_INFO "%s", version);
|
|
}
|
|
|
|
/*
|
|
* 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)
|
|
{
|
|
struct in_chunk_dir cd;
|
|
char *s;
|
|
|
|
if (!ecard_readchunk(&cd, ec, 0xf5, 0)) {
|
|
printk(KERN_ERR "%s: unable to read podule description string\n",
|
|
dev_name(&ec->dev));
|
|
goto no_addr;
|
|
}
|
|
|
|
s = strchr(cd.d.string, '(');
|
|
if (s) {
|
|
int i;
|
|
|
|
for (i = 0; i < 6; i++) {
|
|
addr[i] = simple_strtoul(s + 1, &s, 0x10);
|
|
if (*s != (i == 5? ')' : ':'))
|
|
break;
|
|
}
|
|
|
|
if (i == 6)
|
|
return 0;
|
|
}
|
|
|
|
printk(KERN_ERR "%s: unable to parse MAC address: %s\n",
|
|
dev_name(&ec->dev), cd.d.string);
|
|
|
|
no_addr:
|
|
return -ENODEV;
|
|
}
|
|
|
|
/*
|
|
* Create an ethernet address from the system serial number.
|
|
*/
|
|
static int __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;
|
|
}
|
|
|
|
static void etherh_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
|
|
{
|
|
strlcpy(info->driver, DRV_NAME, sizeof(info->driver));
|
|
strlcpy(info->version, DRV_VERSION, sizeof(info->version));
|
|
strlcpy(info->bus_info, dev_name(dev->dev.parent),
|
|
sizeof(info->bus_info));
|
|
}
|
|
|
|
static int etherh_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
|
|
{
|
|
cmd->supported = etherh_priv(dev)->supported;
|
|
cmd->speed = SPEED_10;
|
|
cmd->duplex = DUPLEX_HALF;
|
|
cmd->port = dev->if_port == IF_PORT_10BASET ? PORT_TP : PORT_BNC;
|
|
cmd->autoneg = dev->flags & IFF_AUTOMEDIA ? AUTONEG_ENABLE : AUTONEG_DISABLE;
|
|
return 0;
|
|
}
|
|
|
|
static int etherh_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
|
|
{
|
|
switch (cmd->autoneg) {
|
|
case AUTONEG_ENABLE:
|
|
dev->flags |= IFF_AUTOMEDIA;
|
|
break;
|
|
|
|
case AUTONEG_DISABLE:
|
|
switch (cmd->port) {
|
|
case PORT_TP:
|
|
dev->if_port = IF_PORT_10BASET;
|
|
break;
|
|
|
|
case PORT_BNC:
|
|
dev->if_port = IF_PORT_10BASE2;
|
|
break;
|
|
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
dev->flags &= ~IFF_AUTOMEDIA;
|
|
break;
|
|
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
etherh_setif(dev);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct ethtool_ops etherh_ethtool_ops = {
|
|
.get_settings = etherh_get_settings,
|
|
.set_settings = etherh_set_settings,
|
|
.get_drvinfo = etherh_get_drvinfo,
|
|
};
|
|
|
|
static const struct net_device_ops etherh_netdev_ops = {
|
|
.ndo_open = etherh_open,
|
|
.ndo_stop = etherh_close,
|
|
.ndo_set_config = etherh_set_config,
|
|
.ndo_start_xmit = __ei_start_xmit,
|
|
.ndo_tx_timeout = __ei_tx_timeout,
|
|
.ndo_get_stats = __ei_get_stats,
|
|
.ndo_set_multicast_list = __ei_set_multicast_list,
|
|
.ndo_validate_addr = eth_validate_addr,
|
|
.ndo_set_mac_address = eth_mac_addr,
|
|
.ndo_change_mtu = eth_change_mtu,
|
|
#ifdef CONFIG_NET_POLL_CONTROLLER
|
|
.ndo_poll_controller = __ei_poll,
|
|
#endif
|
|
};
|
|
|
|
static u32 etherh_regoffsets[16];
|
|
static u32 etherm_regoffsets[16];
|
|
|
|
static int __init
|
|
etherh_probe(struct expansion_card *ec, const struct ecard_id *id)
|
|
{
|
|
const struct etherh_data *data = id->data;
|
|
struct ei_device *ei_local;
|
|
struct net_device *dev;
|
|
struct etherh_priv *eh;
|
|
int ret;
|
|
|
|
etherh_banner();
|
|
|
|
ret = ecard_request_resources(ec);
|
|
if (ret)
|
|
goto out;
|
|
|
|
dev = ____alloc_ei_netdev(sizeof(struct etherh_priv));
|
|
if (!dev) {
|
|
ret = -ENOMEM;
|
|
goto release;
|
|
}
|
|
|
|
SET_NETDEV_DEV(dev, &ec->dev);
|
|
|
|
dev->netdev_ops = ðerh_netdev_ops;
|
|
dev->irq = ec->irq;
|
|
dev->ethtool_ops = ðerh_ethtool_ops;
|
|
|
|
if (data->supported & SUPPORTED_Autoneg)
|
|
dev->flags |= IFF_AUTOMEDIA;
|
|
if (data->supported & SUPPORTED_TP) {
|
|
dev->flags |= IFF_PORTSEL;
|
|
dev->if_port = IF_PORT_10BASET;
|
|
} else if (data->supported & SUPPORTED_BNC) {
|
|
dev->flags |= IFF_PORTSEL;
|
|
dev->if_port = IF_PORT_10BASE2;
|
|
} else
|
|
dev->if_port = IF_PORT_UNKNOWN;
|
|
|
|
eh = etherh_priv(dev);
|
|
eh->supported = data->supported;
|
|
eh->ctrl = 0;
|
|
eh->id = ec->cid.product;
|
|
eh->memc = ecardm_iomap(ec, ECARD_RES_MEMC, 0, PAGE_SIZE);
|
|
if (!eh->memc) {
|
|
ret = -ENOMEM;
|
|
goto free;
|
|
}
|
|
|
|
eh->ctrl_port = eh->memc;
|
|
if (data->ctrl_ioc) {
|
|
eh->ioc_fast = ecardm_iomap(ec, ECARD_RES_IOCFAST, 0, PAGE_SIZE);
|
|
if (!eh->ioc_fast) {
|
|
ret = -ENOMEM;
|
|
goto free;
|
|
}
|
|
eh->ctrl_port = eh->ioc_fast;
|
|
}
|
|
|
|
dev->base_addr = (unsigned long)eh->memc + data->ns8390_offset;
|
|
eh->dma_base = eh->memc + data->dataport_offset;
|
|
eh->ctrl_port += data->ctrlport_offset;
|
|
|
|
/*
|
|
* IRQ and control port handling - only for non-NIC slot cards.
|
|
*/
|
|
if (ec->slot_no != 8) {
|
|
ecard_setirq(ec, ðerh_ops, eh);
|
|
} else {
|
|
/*
|
|
* If we're in the NIC slot, make sure the IRQ is enabled
|
|
*/
|
|
etherh_set_ctrl(eh, ETHERH_CP_IE);
|
|
}
|
|
|
|
ei_local = netdev_priv(dev);
|
|
spin_lock_init(&ei_local->page_lock);
|
|
|
|
if (ec->cid.product == PROD_ANT_ETHERM) {
|
|
etherm_addr(dev->dev_addr);
|
|
ei_local->reg_offset = etherm_regoffsets;
|
|
} else {
|
|
etherh_addr(dev->dev_addr, ec);
|
|
ei_local->reg_offset = etherh_regoffsets;
|
|
}
|
|
|
|
ei_local->name = dev->name;
|
|
ei_local->word16 = 1;
|
|
ei_local->tx_start_page = data->tx_start_page;
|
|
ei_local->rx_start_page = ei_local->tx_start_page + TX_PAGES;
|
|
ei_local->stop_page = data->stop_page;
|
|
ei_local->reset_8390 = etherh_reset;
|
|
ei_local->block_input = etherh_block_input;
|
|
ei_local->block_output = etherh_block_output;
|
|
ei_local->get_8390_hdr = etherh_get_header;
|
|
ei_local->interface_num = 0;
|
|
|
|
etherh_reset(dev);
|
|
__NS8390_init(dev, 0);
|
|
|
|
ret = register_netdev(dev);
|
|
if (ret)
|
|
goto free;
|
|
|
|
printk(KERN_INFO "%s: %s in slot %d, %pM\n",
|
|
dev->name, data->name, ec->slot_no, dev->dev_addr);
|
|
|
|
ecard_set_drvdata(ec, dev);
|
|
|
|
return 0;
|
|
|
|
free:
|
|
free_netdev(dev);
|
|
release:
|
|
ecard_release_resources(ec);
|
|
out:
|
|
return ret;
|
|
}
|
|
|
|
static void __devexit etherh_remove(struct expansion_card *ec)
|
|
{
|
|
struct net_device *dev = ecard_get_drvdata(ec);
|
|
|
|
ecard_set_drvdata(ec, NULL);
|
|
|
|
unregister_netdev(dev);
|
|
|
|
free_netdev(dev);
|
|
|
|
ecard_release_resources(ec);
|
|
}
|
|
|
|
static struct etherh_data etherm_data = {
|
|
.ns8390_offset = ETHERM_NS8390,
|
|
.dataport_offset = ETHERM_NS8390 + ETHERM_DATAPORT,
|
|
.ctrlport_offset = ETHERM_NS8390 + ETHERM_CTRLPORT,
|
|
.name = "ANT EtherM",
|
|
.supported = SUPPORTED_10baseT_Half,
|
|
.tx_start_page = ETHERM_TX_START_PAGE,
|
|
.stop_page = ETHERM_STOP_PAGE,
|
|
};
|
|
|
|
static struct etherh_data etherlan500_data = {
|
|
.ns8390_offset = ETHERH500_NS8390,
|
|
.dataport_offset = ETHERH500_NS8390 + ETHERH500_DATAPORT,
|
|
.ctrlport_offset = ETHERH500_CTRLPORT,
|
|
.ctrl_ioc = 1,
|
|
.name = "i3 EtherH 500",
|
|
.supported = SUPPORTED_10baseT_Half,
|
|
.tx_start_page = ETHERH_TX_START_PAGE,
|
|
.stop_page = ETHERH_STOP_PAGE,
|
|
};
|
|
|
|
static struct etherh_data etherlan600_data = {
|
|
.ns8390_offset = ETHERH600_NS8390,
|
|
.dataport_offset = ETHERH600_NS8390 + ETHERH600_DATAPORT,
|
|
.ctrlport_offset = ETHERH600_NS8390 + ETHERH600_CTRLPORT,
|
|
.name = "i3 EtherH 600",
|
|
.supported = SUPPORTED_10baseT_Half | SUPPORTED_TP | SUPPORTED_BNC | SUPPORTED_Autoneg,
|
|
.tx_start_page = ETHERH_TX_START_PAGE,
|
|
.stop_page = ETHERH_STOP_PAGE,
|
|
};
|
|
|
|
static struct etherh_data etherlan600a_data = {
|
|
.ns8390_offset = ETHERH600_NS8390,
|
|
.dataport_offset = ETHERH600_NS8390 + ETHERH600_DATAPORT,
|
|
.ctrlport_offset = ETHERH600_NS8390 + ETHERH600_CTRLPORT,
|
|
.name = "i3 EtherH 600A",
|
|
.supported = SUPPORTED_10baseT_Half | SUPPORTED_TP | SUPPORTED_BNC | SUPPORTED_Autoneg,
|
|
.tx_start_page = ETHERH_TX_START_PAGE,
|
|
.stop_page = ETHERH_STOP_PAGE,
|
|
};
|
|
|
|
static const struct ecard_id etherh_ids[] = {
|
|
{ MANU_ANT, PROD_ANT_ETHERM, ðerm_data },
|
|
{ MANU_I3, PROD_I3_ETHERLAN500, ðerlan500_data },
|
|
{ MANU_I3, PROD_I3_ETHERLAN600, ðerlan600_data },
|
|
{ MANU_I3, PROD_I3_ETHERLAN600A, ðerlan600a_data },
|
|
{ 0xffff, 0xffff }
|
|
};
|
|
|
|
static struct ecard_driver etherh_driver = {
|
|
.probe = etherh_probe,
|
|
.remove = __devexit_p(etherh_remove),
|
|
.id_table = etherh_ids,
|
|
.drv = {
|
|
.name = DRV_NAME,
|
|
},
|
|
};
|
|
|
|
static int __init etherh_init(void)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < 16; i++) {
|
|
etherh_regoffsets[i] = i << 2;
|
|
etherm_regoffsets[i] = i << 5;
|
|
}
|
|
|
|
return ecard_register_driver(ðerh_driver);
|
|
}
|
|
|
|
static void __exit etherh_exit(void)
|
|
{
|
|
ecard_remove_driver(ðerh_driver);
|
|
}
|
|
|
|
module_init(etherh_init);
|
|
module_exit(etherh_exit);
|