2005-05-05 16:14:15 -06:00
|
|
|
/*
|
2008-02-04 17:02:15 -07:00
|
|
|
* Davicom DM9000 Fast Ethernet driver for Linux.
|
2005-05-05 16:14:15 -06:00
|
|
|
* Copyright (C) 1997 Sten Wang
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU General Public License
|
|
|
|
* as published by the Free Software Foundation; either version 2
|
|
|
|
* of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
2008-02-04 17:02:15 -07:00
|
|
|
* (C) Copyright 1997-1998 DAVICOM Semiconductor,Inc. All Rights Reserved.
|
2005-07-23 10:25:18 -06:00
|
|
|
*
|
2008-02-04 17:02:15 -07:00
|
|
|
* Additional updates, Copyright:
|
|
|
|
* Ben Dooks <ben@simtec.co.uk>
|
|
|
|
* Sascha Hauer <s.hauer@pengutronix.de>
|
2005-05-05 16:14:15 -06:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/ioport.h>
|
|
|
|
#include <linux/netdevice.h>
|
|
|
|
#include <linux/etherdevice.h>
|
|
|
|
#include <linux/init.h>
|
|
|
|
#include <linux/skbuff.h>
|
|
|
|
#include <linux/spinlock.h>
|
|
|
|
#include <linux/crc32.h>
|
|
|
|
#include <linux/mii.h>
|
2008-02-04 17:02:06 -07:00
|
|
|
#include <linux/ethtool.h>
|
2005-05-05 16:14:15 -06:00
|
|
|
#include <linux/dm9000.h>
|
|
|
|
#include <linux/delay.h>
|
2005-10-29 12:07:23 -06:00
|
|
|
#include <linux/platform_device.h>
|
2008-01-23 06:54:50 -07:00
|
|
|
#include <linux/irq.h>
|
include cleanup: Update gfp.h and slab.h includes to prepare for breaking implicit slab.h inclusion from percpu.h
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>
2010-03-24 02:04:11 -06:00
|
|
|
#include <linux/slab.h>
|
2005-05-05 16:14:15 -06:00
|
|
|
|
|
|
|
#include <asm/delay.h>
|
|
|
|
#include <asm/irq.h>
|
|
|
|
#include <asm/io.h>
|
|
|
|
|
|
|
|
#include "dm9000.h"
|
|
|
|
|
|
|
|
/* Board/System/Debug information/definition ---------------- */
|
|
|
|
|
|
|
|
#define DM9000_PHY 0x40 /* PHY address 0x01 */
|
|
|
|
|
2008-06-24 15:16:01 -06:00
|
|
|
#define CARDNAME "dm9000"
|
|
|
|
#define DRV_VERSION "1.31"
|
2005-05-05 16:14:15 -06:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Transmit timeout, default 5 seconds.
|
|
|
|
*/
|
|
|
|
static int watchdog = 5000;
|
|
|
|
module_param(watchdog, int, 0400);
|
|
|
|
MODULE_PARM_DESC(watchdog, "transmit timeout in milliseconds");
|
|
|
|
|
2008-02-04 17:02:10 -07:00
|
|
|
/* DM9000 register address locking.
|
|
|
|
*
|
|
|
|
* The DM9000 uses an address register to control where data written
|
|
|
|
* to the data register goes. This means that the address register
|
|
|
|
* must be preserved over interrupts or similar calls.
|
|
|
|
*
|
|
|
|
* During interrupt and other critical calls, a spinlock is used to
|
|
|
|
* protect the system, but the calls themselves save the address
|
|
|
|
* in the address register in case they are interrupting another
|
|
|
|
* access to the device.
|
|
|
|
*
|
|
|
|
* For general accesses a lock is provided so that calls which are
|
|
|
|
* allowed to sleep are serialised so that the address register does
|
|
|
|
* not need to be saved. This lock also serves to serialise access
|
|
|
|
* to the EEPROM and PHY access registers which are shared between
|
|
|
|
* these two devices.
|
|
|
|
*/
|
|
|
|
|
2008-06-24 15:15:59 -06:00
|
|
|
/* The driver supports the original DM9000E, and now the two newer
|
|
|
|
* devices, DM9000A and DM9000B.
|
|
|
|
*/
|
|
|
|
|
|
|
|
enum dm9000_type {
|
|
|
|
TYPE_DM9000E, /* original DM9000 */
|
|
|
|
TYPE_DM9000A,
|
|
|
|
TYPE_DM9000B
|
|
|
|
};
|
|
|
|
|
2005-05-05 16:14:15 -06:00
|
|
|
/* Structure/enum declaration ------------------------------- */
|
|
|
|
typedef struct board_info {
|
|
|
|
|
2008-06-24 15:16:01 -06:00
|
|
|
void __iomem *io_addr; /* Register I/O base address */
|
|
|
|
void __iomem *io_data; /* Data I/O address */
|
|
|
|
u16 irq; /* IRQ */
|
2005-05-05 16:14:15 -06:00
|
|
|
|
2008-06-24 15:16:01 -06:00
|
|
|
u16 tx_pkt_cnt;
|
|
|
|
u16 queue_pkt_len;
|
|
|
|
u16 queue_start_addr;
|
2009-07-06 19:12:33 -06:00
|
|
|
u16 queue_ip_summed;
|
2008-06-24 15:16:01 -06:00
|
|
|
u16 dbug_cnt;
|
|
|
|
u8 io_mode; /* 0:word, 2:byte */
|
|
|
|
u8 phy_addr;
|
|
|
|
u8 imr_all;
|
|
|
|
|
|
|
|
unsigned int flags;
|
|
|
|
unsigned int in_suspend :1;
|
2009-11-10 00:22:24 -07:00
|
|
|
unsigned int wake_supported :1;
|
2008-06-24 15:16:01 -06:00
|
|
|
int debug_level;
|
2005-05-05 16:14:15 -06:00
|
|
|
|
2008-06-24 15:15:59 -06:00
|
|
|
enum dm9000_type type;
|
2008-02-04 17:02:03 -07:00
|
|
|
|
2005-05-05 16:14:15 -06:00
|
|
|
void (*inblk)(void __iomem *port, void *data, int length);
|
|
|
|
void (*outblk)(void __iomem *port, void *data, int length);
|
|
|
|
void (*dumpblk)(void __iomem *port, int length);
|
|
|
|
|
2008-02-04 17:02:02 -07:00
|
|
|
struct device *dev; /* parent device */
|
|
|
|
|
2005-05-05 16:14:15 -06:00
|
|
|
struct resource *addr_res; /* resources found */
|
|
|
|
struct resource *data_res;
|
|
|
|
struct resource *addr_req; /* resources requested */
|
|
|
|
struct resource *data_req;
|
|
|
|
struct resource *irq_res;
|
|
|
|
|
2009-11-10 00:22:24 -07:00
|
|
|
int irq_wake;
|
|
|
|
|
2008-02-04 17:02:10 -07:00
|
|
|
struct mutex addr_lock; /* phy and eeprom access lock */
|
|
|
|
|
2008-05-08 04:36:42 -06:00
|
|
|
struct delayed_work phy_poll;
|
|
|
|
struct net_device *ndev;
|
|
|
|
|
2008-06-24 15:16:01 -06:00
|
|
|
spinlock_t lock;
|
2005-05-05 16:14:15 -06:00
|
|
|
|
|
|
|
struct mii_if_info mii;
|
2008-06-24 15:16:01 -06:00
|
|
|
u32 msg_enable;
|
2009-11-10 00:22:24 -07:00
|
|
|
u32 wake_state;
|
2009-07-06 19:12:33 -06:00
|
|
|
|
|
|
|
int rx_csum;
|
|
|
|
int can_csum;
|
|
|
|
int ip_summed;
|
2005-05-05 16:14:15 -06:00
|
|
|
} board_info_t;
|
|
|
|
|
2008-02-04 17:02:03 -07:00
|
|
|
/* debug code */
|
|
|
|
|
|
|
|
#define dm9000_dbg(db, lev, msg...) do { \
|
|
|
|
if ((lev) < CONFIG_DM9000_DEBUGLEVEL && \
|
|
|
|
(lev) < db->debug_level) { \
|
|
|
|
dev_dbg(db->dev, msg); \
|
|
|
|
} \
|
|
|
|
} while (0)
|
|
|
|
|
2008-02-04 17:02:06 -07:00
|
|
|
static inline board_info_t *to_dm9000_board(struct net_device *dev)
|
|
|
|
{
|
2008-11-13 00:38:14 -07:00
|
|
|
return netdev_priv(dev);
|
2008-02-04 17:02:06 -07:00
|
|
|
}
|
|
|
|
|
2005-05-05 16:14:15 -06:00
|
|
|
/* DM9000 network board routine ---------------------------- */
|
|
|
|
|
|
|
|
static void
|
|
|
|
dm9000_reset(board_info_t * db)
|
|
|
|
{
|
2008-02-04 17:02:02 -07:00
|
|
|
dev_dbg(db->dev, "resetting device\n");
|
|
|
|
|
2005-05-05 16:14:15 -06:00
|
|
|
/* RESET device */
|
|
|
|
writeb(DM9000_NCR, db->io_addr);
|
|
|
|
udelay(200);
|
|
|
|
writeb(NCR_RST, db->io_data);
|
|
|
|
udelay(200);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Read a byte from I/O port
|
|
|
|
*/
|
|
|
|
static u8
|
|
|
|
ior(board_info_t * db, int reg)
|
|
|
|
{
|
|
|
|
writeb(reg, db->io_addr);
|
|
|
|
return readb(db->io_data);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Write a byte to I/O port
|
|
|
|
*/
|
|
|
|
|
|
|
|
static void
|
|
|
|
iow(board_info_t * db, int reg, int value)
|
|
|
|
{
|
|
|
|
writeb(reg, db->io_addr);
|
|
|
|
writeb(value, db->io_data);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* routines for sending block to chip */
|
|
|
|
|
|
|
|
static void dm9000_outblk_8bit(void __iomem *reg, void *data, int count)
|
|
|
|
{
|
|
|
|
writesb(reg, data, count);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void dm9000_outblk_16bit(void __iomem *reg, void *data, int count)
|
|
|
|
{
|
|
|
|
writesw(reg, data, (count+1) >> 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void dm9000_outblk_32bit(void __iomem *reg, void *data, int count)
|
|
|
|
{
|
|
|
|
writesl(reg, data, (count+3) >> 2);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* input block from chip to memory */
|
|
|
|
|
|
|
|
static void dm9000_inblk_8bit(void __iomem *reg, void *data, int count)
|
|
|
|
{
|
2005-06-20 16:32:51 -06:00
|
|
|
readsb(reg, data, count);
|
2005-05-05 16:14:15 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void dm9000_inblk_16bit(void __iomem *reg, void *data, int count)
|
|
|
|
{
|
|
|
|
readsw(reg, data, (count+1) >> 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void dm9000_inblk_32bit(void __iomem *reg, void *data, int count)
|
|
|
|
{
|
|
|
|
readsl(reg, data, (count+3) >> 2);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* dump block from chip to null */
|
|
|
|
|
|
|
|
static void dm9000_dumpblk_8bit(void __iomem *reg, int count)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
int tmp;
|
|
|
|
|
|
|
|
for (i = 0; i < count; i++)
|
|
|
|
tmp = readb(reg);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void dm9000_dumpblk_16bit(void __iomem *reg, int count)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
int tmp;
|
|
|
|
|
|
|
|
count = (count + 1) >> 1;
|
|
|
|
|
|
|
|
for (i = 0; i < count; i++)
|
|
|
|
tmp = readw(reg);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void dm9000_dumpblk_32bit(void __iomem *reg, int count)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
int tmp;
|
|
|
|
|
|
|
|
count = (count + 3) >> 2;
|
|
|
|
|
|
|
|
for (i = 0; i < count; i++)
|
|
|
|
tmp = readl(reg);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* dm9000_set_io
|
|
|
|
*
|
|
|
|
* select the specified set of io routines to use with the
|
|
|
|
* device
|
|
|
|
*/
|
|
|
|
|
|
|
|
static void dm9000_set_io(struct board_info *db, int byte_width)
|
|
|
|
{
|
|
|
|
/* use the size of the data resource to work out what IO
|
|
|
|
* routines we want to use
|
|
|
|
*/
|
|
|
|
|
|
|
|
switch (byte_width) {
|
|
|
|
case 1:
|
|
|
|
db->dumpblk = dm9000_dumpblk_8bit;
|
|
|
|
db->outblk = dm9000_outblk_8bit;
|
|
|
|
db->inblk = dm9000_inblk_8bit;
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case 3:
|
2008-02-04 17:02:02 -07:00
|
|
|
dev_dbg(db->dev, ": 3 byte IO, falling back to 16bit\n");
|
|
|
|
case 2:
|
2005-05-05 16:14:15 -06:00
|
|
|
db->dumpblk = dm9000_dumpblk_16bit;
|
|
|
|
db->outblk = dm9000_outblk_16bit;
|
|
|
|
db->inblk = dm9000_inblk_16bit;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 4:
|
|
|
|
default:
|
|
|
|
db->dumpblk = dm9000_dumpblk_32bit;
|
|
|
|
db->outblk = dm9000_outblk_32bit;
|
|
|
|
db->inblk = dm9000_inblk_32bit;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-05-08 04:36:42 -06:00
|
|
|
static void dm9000_schedule_poll(board_info_t *db)
|
|
|
|
{
|
2008-06-24 15:15:59 -06:00
|
|
|
if (db->type == TYPE_DM9000E)
|
|
|
|
schedule_delayed_work(&db->phy_poll, HZ * 2);
|
2008-05-08 04:36:42 -06:00
|
|
|
}
|
2005-05-05 16:14:15 -06:00
|
|
|
|
2008-06-24 15:16:02 -06:00
|
|
|
static int dm9000_ioctl(struct net_device *dev, struct ifreq *req, int cmd)
|
|
|
|
{
|
|
|
|
board_info_t *dm = to_dm9000_board(dev);
|
|
|
|
|
|
|
|
if (!netif_running(dev))
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
return generic_mii_ioctl(&dm->mii, if_mii(req), cmd, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static unsigned int
|
|
|
|
dm9000_read_locked(board_info_t *db, int reg)
|
2005-05-05 16:14:15 -06:00
|
|
|
{
|
|
|
|
unsigned long flags;
|
2008-06-24 15:16:02 -06:00
|
|
|
unsigned int ret;
|
2005-05-05 16:14:15 -06:00
|
|
|
|
2008-06-24 15:16:02 -06:00
|
|
|
spin_lock_irqsave(&db->lock, flags);
|
|
|
|
ret = ior(db, reg);
|
|
|
|
spin_unlock_irqrestore(&db->lock, flags);
|
2005-05-05 16:14:15 -06:00
|
|
|
|
2008-06-24 15:16:02 -06:00
|
|
|
return ret;
|
|
|
|
}
|
2005-05-05 16:14:15 -06:00
|
|
|
|
2008-06-24 15:16:02 -06:00
|
|
|
static int dm9000_wait_eeprom(board_info_t *db)
|
|
|
|
{
|
|
|
|
unsigned int status;
|
|
|
|
int timeout = 8; /* wait max 8msec */
|
|
|
|
|
|
|
|
/* The DM9000 data sheets say we should be able to
|
|
|
|
* poll the ERRE bit in EPCR to wait for the EEPROM
|
|
|
|
* operation. From testing several chips, this bit
|
|
|
|
* does not seem to work.
|
|
|
|
*
|
|
|
|
* We attempt to use the bit, but fall back to the
|
|
|
|
* timeout (which is why we do not return an error
|
|
|
|
* on expiry) to say that the EEPROM operation has
|
|
|
|
* completed.
|
|
|
|
*/
|
|
|
|
|
|
|
|
while (1) {
|
|
|
|
status = dm9000_read_locked(db, DM9000_EPCR);
|
|
|
|
|
|
|
|
if ((status & EPCR_ERRE) == 0)
|
|
|
|
break;
|
|
|
|
|
2008-06-24 15:16:05 -06:00
|
|
|
msleep(1);
|
|
|
|
|
2008-06-24 15:16:02 -06:00
|
|
|
if (timeout-- < 0) {
|
|
|
|
dev_dbg(db->dev, "timeout waiting EEPROM\n");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
2005-05-05 16:14:15 -06:00
|
|
|
}
|
|
|
|
|
2006-08-15 00:00:15 -06:00
|
|
|
/*
|
2008-06-24 15:16:02 -06:00
|
|
|
* Read a word data from EEPROM
|
2006-08-15 00:00:15 -06:00
|
|
|
*/
|
2008-06-24 15:16:02 -06:00
|
|
|
static void
|
|
|
|
dm9000_read_eeprom(board_info_t *db, int offset, u8 *to)
|
2006-08-15 00:00:15 -06:00
|
|
|
{
|
2008-06-24 15:16:02 -06:00
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
if (db->flags & DM9000_PLATF_NO_EEPROM) {
|
|
|
|
to[0] = 0xff;
|
|
|
|
to[1] = 0xff;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
mutex_lock(&db->addr_lock);
|
|
|
|
|
|
|
|
spin_lock_irqsave(&db->lock, flags);
|
|
|
|
|
|
|
|
iow(db, DM9000_EPAR, offset);
|
|
|
|
iow(db, DM9000_EPCR, EPCR_ERPRR);
|
|
|
|
|
|
|
|
spin_unlock_irqrestore(&db->lock, flags);
|
|
|
|
|
|
|
|
dm9000_wait_eeprom(db);
|
|
|
|
|
|
|
|
/* delay for at-least 150uS */
|
|
|
|
msleep(1);
|
|
|
|
|
|
|
|
spin_lock_irqsave(&db->lock, flags);
|
|
|
|
|
|
|
|
iow(db, DM9000_EPCR, 0x0);
|
|
|
|
|
|
|
|
to[0] = ior(db, DM9000_EPDRL);
|
|
|
|
to[1] = ior(db, DM9000_EPDRH);
|
|
|
|
|
|
|
|
spin_unlock_irqrestore(&db->lock, flags);
|
|
|
|
|
|
|
|
mutex_unlock(&db->addr_lock);
|
2006-08-15 00:00:15 -06:00
|
|
|
}
|
2005-05-05 16:14:15 -06:00
|
|
|
|
2008-06-24 15:16:02 -06:00
|
|
|
/*
|
|
|
|
* Write a word data to SROM
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
dm9000_write_eeprom(board_info_t *db, int offset, u8 *data)
|
2008-02-04 17:02:21 -07:00
|
|
|
{
|
2008-06-24 15:16:02 -06:00
|
|
|
unsigned long flags;
|
2008-02-04 17:02:21 -07:00
|
|
|
|
2008-06-24 15:16:02 -06:00
|
|
|
if (db->flags & DM9000_PLATF_NO_EEPROM)
|
|
|
|
return;
|
2008-02-04 17:02:21 -07:00
|
|
|
|
2008-06-24 15:16:02 -06:00
|
|
|
mutex_lock(&db->addr_lock);
|
|
|
|
|
|
|
|
spin_lock_irqsave(&db->lock, flags);
|
|
|
|
iow(db, DM9000_EPAR, offset);
|
|
|
|
iow(db, DM9000_EPDRH, data[1]);
|
|
|
|
iow(db, DM9000_EPDRL, data[0]);
|
|
|
|
iow(db, DM9000_EPCR, EPCR_WEP | EPCR_ERPRW);
|
|
|
|
spin_unlock_irqrestore(&db->lock, flags);
|
|
|
|
|
|
|
|
dm9000_wait_eeprom(db);
|
|
|
|
|
|
|
|
mdelay(1); /* wait at least 150uS to clear */
|
|
|
|
|
|
|
|
spin_lock_irqsave(&db->lock, flags);
|
|
|
|
iow(db, DM9000_EPCR, 0);
|
|
|
|
spin_unlock_irqrestore(&db->lock, flags);
|
|
|
|
|
|
|
|
mutex_unlock(&db->addr_lock);
|
2008-02-04 17:02:21 -07:00
|
|
|
}
|
|
|
|
|
2008-02-04 17:02:06 -07:00
|
|
|
/* ethtool ops */
|
|
|
|
|
|
|
|
static void dm9000_get_drvinfo(struct net_device *dev,
|
|
|
|
struct ethtool_drvinfo *info)
|
|
|
|
{
|
|
|
|
board_info_t *dm = to_dm9000_board(dev);
|
|
|
|
|
|
|
|
strcpy(info->driver, CARDNAME);
|
|
|
|
strcpy(info->version, DRV_VERSION);
|
|
|
|
strcpy(info->bus_info, to_platform_device(dm->dev)->name);
|
|
|
|
}
|
|
|
|
|
2008-02-04 17:02:12 -07:00
|
|
|
static u32 dm9000_get_msglevel(struct net_device *dev)
|
|
|
|
{
|
|
|
|
board_info_t *dm = to_dm9000_board(dev);
|
|
|
|
|
|
|
|
return dm->msg_enable;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void dm9000_set_msglevel(struct net_device *dev, u32 value)
|
|
|
|
{
|
|
|
|
board_info_t *dm = to_dm9000_board(dev);
|
|
|
|
|
|
|
|
dm->msg_enable = value;
|
|
|
|
}
|
|
|
|
|
2008-02-04 17:02:06 -07:00
|
|
|
static int dm9000_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
|
|
|
|
{
|
|
|
|
board_info_t *dm = to_dm9000_board(dev);
|
|
|
|
|
|
|
|
mii_ethtool_gset(&dm->mii, cmd);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int dm9000_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
|
|
|
|
{
|
|
|
|
board_info_t *dm = to_dm9000_board(dev);
|
|
|
|
|
2008-02-04 17:02:10 -07:00
|
|
|
return mii_ethtool_sset(&dm->mii, cmd);
|
2008-02-04 17:02:06 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
static int dm9000_nway_reset(struct net_device *dev)
|
|
|
|
{
|
|
|
|
board_info_t *dm = to_dm9000_board(dev);
|
|
|
|
return mii_nway_restart(&dm->mii);
|
|
|
|
}
|
|
|
|
|
2009-07-06 19:12:33 -06:00
|
|
|
static uint32_t dm9000_get_rx_csum(struct net_device *dev)
|
|
|
|
{
|
|
|
|
board_info_t *dm = to_dm9000_board(dev);
|
|
|
|
return dm->rx_csum;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int dm9000_set_rx_csum(struct net_device *dev, uint32_t data)
|
|
|
|
{
|
|
|
|
board_info_t *dm = to_dm9000_board(dev);
|
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
if (dm->can_csum) {
|
|
|
|
dm->rx_csum = data;
|
|
|
|
|
|
|
|
spin_lock_irqsave(&dm->lock, flags);
|
|
|
|
iow(dm, DM9000_RCSR, dm->rx_csum ? RCSR_CSUM : 0);
|
|
|
|
spin_unlock_irqrestore(&dm->lock, flags);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int dm9000_set_tx_csum(struct net_device *dev, uint32_t data)
|
|
|
|
{
|
|
|
|
board_info_t *dm = to_dm9000_board(dev);
|
|
|
|
int ret = -EOPNOTSUPP;
|
|
|
|
|
|
|
|
if (dm->can_csum)
|
|
|
|
ret = ethtool_op_set_tx_csum(dev, data);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2008-02-04 17:02:06 -07:00
|
|
|
static u32 dm9000_get_link(struct net_device *dev)
|
|
|
|
{
|
|
|
|
board_info_t *dm = to_dm9000_board(dev);
|
2008-06-24 15:16:03 -06:00
|
|
|
u32 ret;
|
|
|
|
|
|
|
|
if (dm->flags & DM9000_PLATF_EXT_PHY)
|
|
|
|
ret = mii_link_ok(&dm->mii);
|
|
|
|
else
|
|
|
|
ret = dm9000_read_locked(dm, DM9000_NSR) & NSR_LINKST ? 1 : 0;
|
|
|
|
|
|
|
|
return ret;
|
2008-02-04 17:02:06 -07:00
|
|
|
}
|
|
|
|
|
2008-02-04 17:02:11 -07:00
|
|
|
#define DM_EEPROM_MAGIC (0x444D394B)
|
|
|
|
|
|
|
|
static int dm9000_get_eeprom_len(struct net_device *dev)
|
|
|
|
{
|
|
|
|
return 128;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int dm9000_get_eeprom(struct net_device *dev,
|
|
|
|
struct ethtool_eeprom *ee, u8 *data)
|
|
|
|
{
|
|
|
|
board_info_t *dm = to_dm9000_board(dev);
|
|
|
|
int offset = ee->offset;
|
|
|
|
int len = ee->len;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
/* EEPROM access is aligned to two bytes */
|
|
|
|
|
|
|
|
if ((len & 1) != 0 || (offset & 1) != 0)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2008-02-04 17:02:20 -07:00
|
|
|
if (dm->flags & DM9000_PLATF_NO_EEPROM)
|
|
|
|
return -ENOENT;
|
|
|
|
|
2008-02-04 17:02:11 -07:00
|
|
|
ee->magic = DM_EEPROM_MAGIC;
|
|
|
|
|
|
|
|
for (i = 0; i < len; i += 2)
|
|
|
|
dm9000_read_eeprom(dm, (offset + i) / 2, data + i);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int dm9000_set_eeprom(struct net_device *dev,
|
|
|
|
struct ethtool_eeprom *ee, u8 *data)
|
|
|
|
{
|
|
|
|
board_info_t *dm = to_dm9000_board(dev);
|
|
|
|
int offset = ee->offset;
|
|
|
|
int len = ee->len;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
/* EEPROM access is aligned to two bytes */
|
|
|
|
|
|
|
|
if ((len & 1) != 0 || (offset & 1) != 0)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2008-02-04 17:02:20 -07:00
|
|
|
if (dm->flags & DM9000_PLATF_NO_EEPROM)
|
|
|
|
return -ENOENT;
|
|
|
|
|
2008-02-04 17:02:11 -07:00
|
|
|
if (ee->magic != DM_EEPROM_MAGIC)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
for (i = 0; i < len; i += 2)
|
|
|
|
dm9000_write_eeprom(dm, (offset + i) / 2, data + i);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-11-10 00:22:24 -07:00
|
|
|
static void dm9000_get_wol(struct net_device *dev, struct ethtool_wolinfo *w)
|
|
|
|
{
|
|
|
|
board_info_t *dm = to_dm9000_board(dev);
|
|
|
|
|
|
|
|
memset(w, 0, sizeof(struct ethtool_wolinfo));
|
|
|
|
|
|
|
|
/* note, we could probably support wake-phy too */
|
|
|
|
w->supported = dm->wake_supported ? WAKE_MAGIC : 0;
|
|
|
|
w->wolopts = dm->wake_state;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int dm9000_set_wol(struct net_device *dev, struct ethtool_wolinfo *w)
|
|
|
|
{
|
|
|
|
board_info_t *dm = to_dm9000_board(dev);
|
|
|
|
unsigned long flags;
|
|
|
|
u32 opts = w->wolopts;
|
|
|
|
u32 wcr = 0;
|
|
|
|
|
|
|
|
if (!dm->wake_supported)
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
|
|
if (opts & ~WAKE_MAGIC)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (opts & WAKE_MAGIC)
|
|
|
|
wcr |= WCR_MAGICEN;
|
|
|
|
|
|
|
|
mutex_lock(&dm->addr_lock);
|
|
|
|
|
|
|
|
spin_lock_irqsave(&dm->lock, flags);
|
|
|
|
iow(dm, DM9000_WCR, wcr);
|
|
|
|
spin_unlock_irqrestore(&dm->lock, flags);
|
|
|
|
|
|
|
|
mutex_unlock(&dm->addr_lock);
|
|
|
|
|
|
|
|
if (dm->wake_state != opts) {
|
|
|
|
/* change in wol state, update IRQ state */
|
|
|
|
|
|
|
|
if (!dm->wake_state)
|
|
|
|
set_irq_wake(dm->irq_wake, 1);
|
|
|
|
else if (dm->wake_state & !opts)
|
|
|
|
set_irq_wake(dm->irq_wake, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
dm->wake_state = opts;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2008-02-04 17:02:06 -07:00
|
|
|
static const struct ethtool_ops dm9000_ethtool_ops = {
|
|
|
|
.get_drvinfo = dm9000_get_drvinfo,
|
|
|
|
.get_settings = dm9000_get_settings,
|
|
|
|
.set_settings = dm9000_set_settings,
|
2008-02-04 17:02:12 -07:00
|
|
|
.get_msglevel = dm9000_get_msglevel,
|
|
|
|
.set_msglevel = dm9000_set_msglevel,
|
2008-02-04 17:02:06 -07:00
|
|
|
.nway_reset = dm9000_nway_reset,
|
|
|
|
.get_link = dm9000_get_link,
|
2009-11-10 00:22:24 -07:00
|
|
|
.get_wol = dm9000_get_wol,
|
|
|
|
.set_wol = dm9000_set_wol,
|
2008-02-04 17:02:11 -07:00
|
|
|
.get_eeprom_len = dm9000_get_eeprom_len,
|
|
|
|
.get_eeprom = dm9000_get_eeprom,
|
|
|
|
.set_eeprom = dm9000_set_eeprom,
|
2009-07-06 19:12:33 -06:00
|
|
|
.get_rx_csum = dm9000_get_rx_csum,
|
|
|
|
.set_rx_csum = dm9000_set_rx_csum,
|
|
|
|
.get_tx_csum = ethtool_op_get_tx_csum,
|
|
|
|
.set_tx_csum = dm9000_set_tx_csum,
|
2008-02-04 17:02:06 -07:00
|
|
|
};
|
|
|
|
|
2008-06-24 15:16:04 -06:00
|
|
|
static void dm9000_show_carrier(board_info_t *db,
|
|
|
|
unsigned carrier, unsigned nsr)
|
|
|
|
{
|
|
|
|
struct net_device *ndev = db->ndev;
|
|
|
|
unsigned ncr = dm9000_read_locked(db, DM9000_NCR);
|
|
|
|
|
|
|
|
if (carrier)
|
|
|
|
dev_info(db->dev, "%s: link up, %dMbps, %s-duplex, no LPA\n",
|
|
|
|
ndev->name, (nsr & NSR_SPEED) ? 10 : 100,
|
|
|
|
(ncr & NCR_FDX) ? "full" : "half");
|
|
|
|
else
|
|
|
|
dev_info(db->dev, "%s: link down\n", ndev->name);
|
|
|
|
}
|
|
|
|
|
2008-05-08 04:36:42 -06:00
|
|
|
static void
|
|
|
|
dm9000_poll_work(struct work_struct *w)
|
|
|
|
{
|
2009-04-02 17:56:54 -06:00
|
|
|
struct delayed_work *dw = to_delayed_work(w);
|
2008-05-08 04:36:42 -06:00
|
|
|
board_info_t *db = container_of(dw, board_info_t, phy_poll);
|
2008-06-24 15:16:04 -06:00
|
|
|
struct net_device *ndev = db->ndev;
|
|
|
|
|
|
|
|
if (db->flags & DM9000_PLATF_SIMPLE_PHY &&
|
|
|
|
!(db->flags & DM9000_PLATF_EXT_PHY)) {
|
|
|
|
unsigned nsr = dm9000_read_locked(db, DM9000_NSR);
|
|
|
|
unsigned old_carrier = netif_carrier_ok(ndev) ? 1 : 0;
|
|
|
|
unsigned new_carrier;
|
2008-05-08 04:36:42 -06:00
|
|
|
|
2008-06-24 15:16:04 -06:00
|
|
|
new_carrier = (nsr & NSR_LINKST) ? 1 : 0;
|
|
|
|
|
|
|
|
if (old_carrier != new_carrier) {
|
|
|
|
if (netif_msg_link(db))
|
|
|
|
dm9000_show_carrier(db, new_carrier, nsr);
|
|
|
|
|
|
|
|
if (!new_carrier)
|
|
|
|
netif_carrier_off(ndev);
|
|
|
|
else
|
|
|
|
netif_carrier_on(ndev);
|
|
|
|
}
|
|
|
|
} else
|
|
|
|
mii_check_media(&db->mii, netif_msg_link(db), 0);
|
2008-05-08 04:36:42 -06:00
|
|
|
|
2008-06-24 15:16:04 -06:00
|
|
|
if (netif_running(ndev))
|
2008-05-08 04:36:42 -06:00
|
|
|
dm9000_schedule_poll(db);
|
|
|
|
}
|
2008-02-04 17:02:06 -07:00
|
|
|
|
2005-05-05 16:14:15 -06:00
|
|
|
/* dm9000_release_board
|
|
|
|
*
|
|
|
|
* release a board, and any mapped resources
|
|
|
|
*/
|
|
|
|
|
|
|
|
static void
|
|
|
|
dm9000_release_board(struct platform_device *pdev, struct board_info *db)
|
|
|
|
{
|
|
|
|
/* unmap our resources */
|
|
|
|
|
|
|
|
iounmap(db->io_addr);
|
|
|
|
iounmap(db->io_data);
|
|
|
|
|
|
|
|
/* release the resources */
|
|
|
|
|
2008-06-24 15:16:00 -06:00
|
|
|
release_resource(db->data_req);
|
|
|
|
kfree(db->data_req);
|
2005-05-05 16:14:15 -06:00
|
|
|
|
2008-06-24 15:16:00 -06:00
|
|
|
release_resource(db->addr_req);
|
|
|
|
kfree(db->addr_req);
|
2005-05-05 16:14:15 -06:00
|
|
|
}
|
|
|
|
|
2008-06-24 15:15:59 -06:00
|
|
|
static unsigned char dm9000_type_to_char(enum dm9000_type type)
|
|
|
|
{
|
|
|
|
switch (type) {
|
|
|
|
case TYPE_DM9000E: return 'e';
|
|
|
|
case TYPE_DM9000A: return 'a';
|
|
|
|
case TYPE_DM9000B: return 'b';
|
|
|
|
}
|
|
|
|
|
|
|
|
return '?';
|
|
|
|
}
|
|
|
|
|
2005-05-05 16:14:15 -06:00
|
|
|
/*
|
2008-06-24 15:16:02 -06:00
|
|
|
* Set DM9000 multicast address
|
2005-05-05 16:14:15 -06:00
|
|
|
*/
|
2008-06-24 15:16:02 -06:00
|
|
|
static void
|
|
|
|
dm9000_hash_table(struct net_device *dev)
|
2005-05-05 16:14:15 -06:00
|
|
|
{
|
2008-11-13 00:38:14 -07:00
|
|
|
board_info_t *db = netdev_priv(dev);
|
2010-02-17 04:09:31 -07:00
|
|
|
struct dev_mc_list *mcptr;
|
2008-06-24 15:16:02 -06:00
|
|
|
int i, oft;
|
|
|
|
u32 hash_val;
|
|
|
|
u16 hash_table[4];
|
|
|
|
u8 rcr = RCR_DIS_LONG | RCR_DIS_CRC | RCR_RXEN;
|
|
|
|
unsigned long flags;
|
2005-05-05 16:14:15 -06:00
|
|
|
|
2008-06-24 15:16:02 -06:00
|
|
|
dm9000_dbg(db, 1, "entering %s\n", __func__);
|
2005-05-05 16:14:15 -06:00
|
|
|
|
2008-06-24 15:16:02 -06:00
|
|
|
spin_lock_irqsave(&db->lock, flags);
|
2005-05-05 16:14:15 -06:00
|
|
|
|
2008-06-24 15:16:02 -06:00
|
|
|
for (i = 0, oft = DM9000_PAR; i < 6; i++, oft++)
|
|
|
|
iow(db, oft, dev->dev_addr[i]);
|
2005-05-05 16:14:15 -06:00
|
|
|
|
2008-06-24 15:16:02 -06:00
|
|
|
/* Clear Hash Table */
|
|
|
|
for (i = 0; i < 4; i++)
|
|
|
|
hash_table[i] = 0x0;
|
2008-02-04 17:02:02 -07:00
|
|
|
|
2008-06-24 15:16:02 -06:00
|
|
|
/* broadcast address */
|
|
|
|
hash_table[3] = 0x8000;
|
2005-07-23 10:25:18 -06:00
|
|
|
|
2008-06-24 15:16:02 -06:00
|
|
|
if (dev->flags & IFF_PROMISC)
|
|
|
|
rcr |= RCR_PRMSC;
|
2008-05-08 04:36:42 -06:00
|
|
|
|
2008-06-24 15:16:02 -06:00
|
|
|
if (dev->flags & IFF_ALLMULTI)
|
|
|
|
rcr |= RCR_ALL;
|
2008-06-24 15:15:57 -06:00
|
|
|
|
2008-06-24 15:16:02 -06:00
|
|
|
/* the multicast address in Hash Table : 64 bits */
|
2010-02-17 04:09:31 -07:00
|
|
|
netdev_for_each_mc_addr(mcptr, dev) {
|
2008-06-24 15:16:02 -06:00
|
|
|
hash_val = ether_crc_le(6, mcptr->dmi_addr) & 0x3f;
|
|
|
|
hash_table[hash_val / 16] |= (u16) 1 << (hash_val % 16);
|
2008-06-24 15:15:57 -06:00
|
|
|
}
|
|
|
|
|
2008-06-24 15:16:02 -06:00
|
|
|
/* Write the hash table to MAC MD table */
|
|
|
|
for (i = 0, oft = DM9000_MAR; i < 4; i++) {
|
|
|
|
iow(db, oft++, hash_table[i]);
|
|
|
|
iow(db, oft++, hash_table[i] >> 8);
|
2008-06-24 15:15:57 -06:00
|
|
|
}
|
|
|
|
|
2008-06-24 15:16:02 -06:00
|
|
|
iow(db, DM9000_RCR, rcr);
|
|
|
|
spin_unlock_irqrestore(&db->lock, flags);
|
|
|
|
}
|
2008-06-24 15:15:57 -06:00
|
|
|
|
2008-06-24 15:16:02 -06:00
|
|
|
/*
|
|
|
|
* Initilize dm9000 board
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
dm9000_init_dm9000(struct net_device *dev)
|
|
|
|
{
|
2008-11-13 00:38:14 -07:00
|
|
|
board_info_t *db = netdev_priv(dev);
|
2008-06-24 15:16:02 -06:00
|
|
|
unsigned int imr;
|
2009-11-10 00:22:24 -07:00
|
|
|
unsigned int ncr;
|
2008-06-24 15:15:57 -06:00
|
|
|
|
2008-06-24 15:16:02 -06:00
|
|
|
dm9000_dbg(db, 1, "entering %s\n", __func__);
|
2008-06-24 15:15:57 -06:00
|
|
|
|
2008-06-24 15:16:02 -06:00
|
|
|
/* I/O mode */
|
|
|
|
db->io_mode = ior(db, DM9000_ISR) >> 6; /* ISR bit7:6 keeps I/O mode */
|
2008-06-24 15:15:57 -06:00
|
|
|
|
2009-07-06 19:12:33 -06:00
|
|
|
/* Checksum mode */
|
|
|
|
dm9000_set_rx_csum(dev, db->rx_csum);
|
|
|
|
|
2008-06-24 15:16:02 -06:00
|
|
|
/* GPIO0 on pre-activate PHY */
|
|
|
|
iow(db, DM9000_GPR, 0); /* REG_1F bit0 activate phyxcer */
|
|
|
|
iow(db, DM9000_GPCR, GPCR_GEP_CNTL); /* Let GPIO0 output */
|
|
|
|
iow(db, DM9000_GPR, 0); /* Enable PHY */
|
2008-06-24 15:15:57 -06:00
|
|
|
|
2009-11-10 00:22:24 -07:00
|
|
|
ncr = (db->flags & DM9000_PLATF_EXT_PHY) ? NCR_EXT_PHY : 0;
|
|
|
|
|
|
|
|
/* if wol is needed, then always set NCR_WAKEEN otherwise we end
|
|
|
|
* up dumping the wake events if we disable this. There is already
|
|
|
|
* a wake-mask in DM9000_WCR */
|
|
|
|
if (db->wake_supported)
|
|
|
|
ncr |= NCR_WAKEEN;
|
|
|
|
|
|
|
|
iow(db, DM9000_NCR, ncr);
|
2008-02-04 17:02:01 -07:00
|
|
|
|
2005-05-05 16:14:15 -06:00
|
|
|
/* Program operating register */
|
|
|
|
iow(db, DM9000_TCR, 0); /* TX Polling clear */
|
|
|
|
iow(db, DM9000_BPTR, 0x3f); /* Less 3Kb, 200us */
|
|
|
|
iow(db, DM9000_FCR, 0xff); /* Flow Control */
|
|
|
|
iow(db, DM9000_SMCR, 0); /* Special Mode */
|
|
|
|
/* clear TX status */
|
|
|
|
iow(db, DM9000_NSR, NSR_WAKEST | NSR_TX2END | NSR_TX1END);
|
|
|
|
iow(db, DM9000_ISR, ISR_CLR_STATUS); /* Clear interrupt status */
|
|
|
|
|
|
|
|
/* Set address filter table */
|
|
|
|
dm9000_hash_table(dev);
|
|
|
|
|
2008-06-24 15:15:59 -06:00
|
|
|
imr = IMR_PAR | IMR_PTM | IMR_PRM;
|
|
|
|
if (db->type != TYPE_DM9000E)
|
|
|
|
imr |= IMR_LNKCHNG;
|
|
|
|
|
|
|
|
db->imr_all = imr;
|
|
|
|
|
2005-05-05 16:14:15 -06:00
|
|
|
/* Enable TX/RX interrupt mask */
|
2008-06-24 15:15:59 -06:00
|
|
|
iow(db, DM9000_IMR, imr);
|
2005-05-05 16:14:15 -06:00
|
|
|
|
|
|
|
/* Init Driver variable */
|
|
|
|
db->tx_pkt_cnt = 0;
|
|
|
|
db->queue_pkt_len = 0;
|
|
|
|
dev->trans_start = 0;
|
|
|
|
}
|
|
|
|
|
2008-06-24 15:16:02 -06:00
|
|
|
/* Our watchdog timed out. Called by the networking layer */
|
|
|
|
static void dm9000_timeout(struct net_device *dev)
|
|
|
|
{
|
2008-11-13 00:38:14 -07:00
|
|
|
board_info_t *db = netdev_priv(dev);
|
2008-06-24 15:16:02 -06:00
|
|
|
u8 reg_save;
|
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
/* Save previous register address */
|
|
|
|
reg_save = readb(db->io_addr);
|
|
|
|
spin_lock_irqsave(&db->lock, flags);
|
|
|
|
|
|
|
|
netif_stop_queue(dev);
|
|
|
|
dm9000_reset(db);
|
|
|
|
dm9000_init_dm9000(dev);
|
|
|
|
/* We can accept TX packets again */
|
|
|
|
dev->trans_start = jiffies;
|
|
|
|
netif_wake_queue(dev);
|
|
|
|
|
|
|
|
/* Restore previous register address */
|
|
|
|
writeb(reg_save, db->io_addr);
|
|
|
|
spin_unlock_irqrestore(&db->lock, flags);
|
|
|
|
}
|
|
|
|
|
2009-07-06 19:12:33 -06:00
|
|
|
static void dm9000_send_packet(struct net_device *dev,
|
|
|
|
int ip_summed,
|
|
|
|
u16 pkt_len)
|
|
|
|
{
|
|
|
|
board_info_t *dm = to_dm9000_board(dev);
|
|
|
|
|
|
|
|
/* The DM9000 is not smart enough to leave fragmented packets alone. */
|
|
|
|
if (dm->ip_summed != ip_summed) {
|
|
|
|
if (ip_summed == CHECKSUM_NONE)
|
|
|
|
iow(dm, DM9000_TCCR, 0);
|
|
|
|
else
|
|
|
|
iow(dm, DM9000_TCCR, TCCR_IP | TCCR_UDP | TCCR_TCP);
|
|
|
|
dm->ip_summed = ip_summed;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Set TX length to DM9000 */
|
|
|
|
iow(dm, DM9000_TXPLL, pkt_len);
|
|
|
|
iow(dm, DM9000_TXPLH, pkt_len >> 8);
|
|
|
|
|
|
|
|
/* Issue TX polling command */
|
|
|
|
iow(dm, DM9000_TCR, TCR_TXREQ); /* Cleared after TX complete */
|
|
|
|
}
|
|
|
|
|
2005-05-05 16:14:15 -06:00
|
|
|
/*
|
|
|
|
* Hardware start transmission.
|
|
|
|
* Send a packet to media from the upper layer.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
dm9000_start_xmit(struct sk_buff *skb, struct net_device *dev)
|
|
|
|
{
|
2007-08-20 17:33:42 -06:00
|
|
|
unsigned long flags;
|
2008-11-13 00:38:14 -07:00
|
|
|
board_info_t *db = netdev_priv(dev);
|
2005-05-05 16:14:15 -06:00
|
|
|
|
2008-02-04 17:02:03 -07:00
|
|
|
dm9000_dbg(db, 3, "%s:\n", __func__);
|
2005-05-05 16:14:15 -06:00
|
|
|
|
|
|
|
if (db->tx_pkt_cnt > 1)
|
2009-06-12 00:22:29 -06:00
|
|
|
return NETDEV_TX_BUSY;
|
2005-05-05 16:14:15 -06:00
|
|
|
|
2007-08-20 17:33:42 -06:00
|
|
|
spin_lock_irqsave(&db->lock, flags);
|
2005-05-05 16:14:15 -06:00
|
|
|
|
|
|
|
/* Move data to DM9000 TX RAM */
|
|
|
|
writeb(DM9000_MWCMD, db->io_addr);
|
|
|
|
|
|
|
|
(db->outblk)(db->io_data, skb->data, skb->len);
|
2007-10-03 18:41:50 -06:00
|
|
|
dev->stats.tx_bytes += skb->len;
|
2005-05-05 16:14:15 -06:00
|
|
|
|
2007-08-20 17:33:42 -06:00
|
|
|
db->tx_pkt_cnt++;
|
2005-05-05 16:14:15 -06:00
|
|
|
/* TX control: First packet immediately send, second packet queue */
|
2007-08-20 17:33:42 -06:00
|
|
|
if (db->tx_pkt_cnt == 1) {
|
2009-07-06 19:12:33 -06:00
|
|
|
dm9000_send_packet(dev, skb->ip_summed, skb->len);
|
2005-05-05 16:14:15 -06:00
|
|
|
} else {
|
|
|
|
/* Second packet */
|
|
|
|
db->queue_pkt_len = skb->len;
|
2009-07-06 19:12:33 -06:00
|
|
|
db->queue_ip_summed = skb->ip_summed;
|
2007-08-20 17:33:42 -06:00
|
|
|
netif_stop_queue(dev);
|
2005-05-05 16:14:15 -06:00
|
|
|
}
|
|
|
|
|
2007-08-20 17:33:42 -06:00
|
|
|
spin_unlock_irqrestore(&db->lock, flags);
|
|
|
|
|
2005-05-05 16:14:15 -06:00
|
|
|
/* free this SKB */
|
|
|
|
dev_kfree_skb(skb);
|
|
|
|
|
2009-06-23 00:03:08 -06:00
|
|
|
return NETDEV_TX_OK;
|
2005-05-05 16:14:15 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2008-06-24 15:16:02 -06:00
|
|
|
* DM9000 interrupt handler
|
|
|
|
* receive the packet to upper layer, free the transmitted packet
|
2005-05-05 16:14:15 -06:00
|
|
|
*/
|
2008-06-24 15:16:02 -06:00
|
|
|
|
|
|
|
static void dm9000_tx_done(struct net_device *dev, board_info_t *db)
|
2005-05-05 16:14:15 -06:00
|
|
|
{
|
2008-06-24 15:16:02 -06:00
|
|
|
int tx_status = ior(db, DM9000_NSR); /* Got TX status */
|
2005-05-05 16:14:15 -06:00
|
|
|
|
2008-06-24 15:16:02 -06:00
|
|
|
if (tx_status & (NSR_TX2END | NSR_TX1END)) {
|
|
|
|
/* One packet sent complete */
|
|
|
|
db->tx_pkt_cnt--;
|
|
|
|
dev->stats.tx_packets++;
|
2005-05-05 16:14:15 -06:00
|
|
|
|
2008-06-24 15:16:02 -06:00
|
|
|
if (netif_msg_tx_done(db))
|
|
|
|
dev_dbg(db->dev, "tx done, NSR %02x\n", tx_status);
|
2008-02-04 17:02:16 -07:00
|
|
|
|
2005-05-05 16:14:15 -06:00
|
|
|
/* Queue packet check & send */
|
2009-07-06 19:12:33 -06:00
|
|
|
if (db->tx_pkt_cnt > 0)
|
|
|
|
dm9000_send_packet(dev, db->queue_ip_summed,
|
|
|
|
db->queue_pkt_len);
|
2005-05-05 16:14:15 -06:00
|
|
|
netif_wake_queue(dev);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
struct dm9000_rxhdr {
|
2008-02-04 17:02:00 -07:00
|
|
|
u8 RxPktReady;
|
|
|
|
u8 RxStatus;
|
2008-03-28 21:11:08 -06:00
|
|
|
__le16 RxLen;
|
2005-05-05 16:14:15 -06:00
|
|
|
} __attribute__((__packed__));
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Received a packet and pass to upper layer
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
dm9000_rx(struct net_device *dev)
|
|
|
|
{
|
2008-11-13 00:38:14 -07:00
|
|
|
board_info_t *db = netdev_priv(dev);
|
2005-05-05 16:14:15 -06:00
|
|
|
struct dm9000_rxhdr rxhdr;
|
|
|
|
struct sk_buff *skb;
|
|
|
|
u8 rxbyte, *rdptr;
|
2007-05-01 10:43:27 -06:00
|
|
|
bool GoodPacket;
|
2005-05-05 16:14:15 -06:00
|
|
|
int RxLen;
|
|
|
|
|
|
|
|
/* Check packet ready or not */
|
|
|
|
do {
|
|
|
|
ior(db, DM9000_MRCMDX); /* Dummy read */
|
|
|
|
|
|
|
|
/* Get most updated data */
|
|
|
|
rxbyte = readb(db->io_data);
|
|
|
|
|
|
|
|
/* Status check: this byte must be 0 or 1 */
|
2009-07-06 19:12:33 -06:00
|
|
|
if (rxbyte & DM9000_PKT_ERR) {
|
2008-02-04 17:02:02 -07:00
|
|
|
dev_warn(db->dev, "status check fail: %d\n", rxbyte);
|
2005-05-05 16:14:15 -06:00
|
|
|
iow(db, DM9000_RCR, 0x00); /* Stop Device */
|
|
|
|
iow(db, DM9000_ISR, IMR_PAR); /* Stop INT request */
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2009-07-06 19:12:33 -06:00
|
|
|
if (!(rxbyte & DM9000_PKT_RDY))
|
2005-05-05 16:14:15 -06:00
|
|
|
return;
|
|
|
|
|
|
|
|
/* A packet ready now & Get status/length */
|
2007-05-01 10:43:27 -06:00
|
|
|
GoodPacket = true;
|
2005-05-05 16:14:15 -06:00
|
|
|
writeb(DM9000_MRCMD, db->io_addr);
|
|
|
|
|
|
|
|
(db->inblk)(db->io_data, &rxhdr, sizeof(rxhdr));
|
|
|
|
|
2008-02-04 17:02:00 -07:00
|
|
|
RxLen = le16_to_cpu(rxhdr.RxLen);
|
2005-05-05 16:14:15 -06:00
|
|
|
|
2008-02-04 17:02:16 -07:00
|
|
|
if (netif_msg_rx_status(db))
|
|
|
|
dev_dbg(db->dev, "RX: status %02x, length %04x\n",
|
|
|
|
rxhdr.RxStatus, RxLen);
|
|
|
|
|
2005-05-05 16:14:15 -06:00
|
|
|
/* Packet Status check */
|
|
|
|
if (RxLen < 0x40) {
|
2007-05-01 10:43:27 -06:00
|
|
|
GoodPacket = false;
|
2008-02-04 17:02:16 -07:00
|
|
|
if (netif_msg_rx_err(db))
|
|
|
|
dev_dbg(db->dev, "RX: Bad Packet (runt)\n");
|
2005-05-05 16:14:15 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
if (RxLen > DM9000_PKT_MAX) {
|
2008-02-04 17:02:02 -07:00
|
|
|
dev_dbg(db->dev, "RST: RX Len:%x\n", RxLen);
|
2005-05-05 16:14:15 -06:00
|
|
|
}
|
|
|
|
|
2008-07-17 13:29:13 -06:00
|
|
|
/* rxhdr.RxStatus is identical to RSR register. */
|
|
|
|
if (rxhdr.RxStatus & (RSR_FOE | RSR_CE | RSR_AE |
|
|
|
|
RSR_PLE | RSR_RWTO |
|
|
|
|
RSR_LCS | RSR_RF)) {
|
2007-05-01 10:43:27 -06:00
|
|
|
GoodPacket = false;
|
2008-07-17 13:29:13 -06:00
|
|
|
if (rxhdr.RxStatus & RSR_FOE) {
|
2008-02-04 17:02:16 -07:00
|
|
|
if (netif_msg_rx_err(db))
|
|
|
|
dev_dbg(db->dev, "fifo error\n");
|
2007-10-03 18:41:50 -06:00
|
|
|
dev->stats.rx_fifo_errors++;
|
2005-05-05 16:14:15 -06:00
|
|
|
}
|
2008-07-17 13:29:13 -06:00
|
|
|
if (rxhdr.RxStatus & RSR_CE) {
|
2008-02-04 17:02:16 -07:00
|
|
|
if (netif_msg_rx_err(db))
|
|
|
|
dev_dbg(db->dev, "crc error\n");
|
2007-10-03 18:41:50 -06:00
|
|
|
dev->stats.rx_crc_errors++;
|
2005-05-05 16:14:15 -06:00
|
|
|
}
|
2008-07-17 13:29:13 -06:00
|
|
|
if (rxhdr.RxStatus & RSR_RF) {
|
2008-02-04 17:02:16 -07:00
|
|
|
if (netif_msg_rx_err(db))
|
|
|
|
dev_dbg(db->dev, "length error\n");
|
2007-10-03 18:41:50 -06:00
|
|
|
dev->stats.rx_length_errors++;
|
2005-05-05 16:14:15 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Move data from DM9000 */
|
2009-12-03 00:58:21 -07:00
|
|
|
if (GoodPacket &&
|
|
|
|
((skb = dev_alloc_skb(RxLen + 4)) != NULL)) {
|
2005-05-05 16:14:15 -06:00
|
|
|
skb_reserve(skb, 2);
|
|
|
|
rdptr = (u8 *) skb_put(skb, RxLen - 4);
|
|
|
|
|
|
|
|
/* Read received packet from RX SRAM */
|
|
|
|
|
|
|
|
(db->inblk)(db->io_data, rdptr, RxLen);
|
2007-10-03 18:41:50 -06:00
|
|
|
dev->stats.rx_bytes += RxLen;
|
2005-05-05 16:14:15 -06:00
|
|
|
|
|
|
|
/* Pass to upper layer */
|
|
|
|
skb->protocol = eth_type_trans(skb, dev);
|
2009-07-06 19:12:33 -06:00
|
|
|
if (db->rx_csum) {
|
|
|
|
if ((((rxbyte & 0x1c) << 3) & rxbyte) == 0)
|
|
|
|
skb->ip_summed = CHECKSUM_UNNECESSARY;
|
|
|
|
else
|
|
|
|
skb->ip_summed = CHECKSUM_NONE;
|
|
|
|
}
|
2005-05-05 16:14:15 -06:00
|
|
|
netif_rx(skb);
|
2007-10-03 18:41:50 -06:00
|
|
|
dev->stats.rx_packets++;
|
2005-05-05 16:14:15 -06:00
|
|
|
|
|
|
|
} else {
|
|
|
|
/* need to dump the packet's data */
|
|
|
|
|
|
|
|
(db->dumpblk)(db->io_data, RxLen);
|
|
|
|
}
|
2009-07-06 19:12:33 -06:00
|
|
|
} while (rxbyte & DM9000_PKT_RDY);
|
2005-05-05 16:14:15 -06:00
|
|
|
}
|
|
|
|
|
2008-06-24 15:16:02 -06:00
|
|
|
static irqreturn_t dm9000_interrupt(int irq, void *dev_id)
|
2008-02-04 17:02:17 -07:00
|
|
|
{
|
2008-06-24 15:16:02 -06:00
|
|
|
struct net_device *dev = dev_id;
|
2008-11-13 00:38:14 -07:00
|
|
|
board_info_t *db = netdev_priv(dev);
|
2008-06-24 15:16:02 -06:00
|
|
|
int int_status;
|
2009-03-22 22:28:39 -06:00
|
|
|
unsigned long flags;
|
2008-06-24 15:16:02 -06:00
|
|
|
u8 reg_save;
|
2008-02-04 17:02:17 -07:00
|
|
|
|
2008-06-24 15:16:02 -06:00
|
|
|
dm9000_dbg(db, 3, "entering %s\n", __func__);
|
2008-02-04 17:02:17 -07:00
|
|
|
|
2008-06-24 15:16:02 -06:00
|
|
|
/* A real interrupt coming */
|
2008-02-04 17:02:17 -07:00
|
|
|
|
2009-03-22 22:28:39 -06:00
|
|
|
/* holders of db->lock must always block IRQs */
|
|
|
|
spin_lock_irqsave(&db->lock, flags);
|
2008-02-04 17:02:17 -07:00
|
|
|
|
2008-06-24 15:16:02 -06:00
|
|
|
/* Save previous register address */
|
|
|
|
reg_save = readb(db->io_addr);
|
2008-02-04 17:02:17 -07:00
|
|
|
|
2008-06-24 15:16:02 -06:00
|
|
|
/* Disable all interrupts */
|
|
|
|
iow(db, DM9000_IMR, IMR_PAR);
|
2008-02-04 17:02:17 -07:00
|
|
|
|
2008-06-24 15:16:02 -06:00
|
|
|
/* Got DM9000 interrupt status */
|
|
|
|
int_status = ior(db, DM9000_ISR); /* Got ISR */
|
|
|
|
iow(db, DM9000_ISR, int_status); /* Clear ISR status */
|
2008-02-04 17:02:17 -07:00
|
|
|
|
2008-06-24 15:16:02 -06:00
|
|
|
if (netif_msg_intr(db))
|
|
|
|
dev_dbg(db->dev, "interrupt status %02x\n", int_status);
|
|
|
|
|
|
|
|
/* Received the coming packet */
|
|
|
|
if (int_status & ISR_PRS)
|
|
|
|
dm9000_rx(dev);
|
|
|
|
|
|
|
|
/* Trnasmit Interrupt check */
|
|
|
|
if (int_status & ISR_PTS)
|
|
|
|
dm9000_tx_done(dev, db);
|
|
|
|
|
|
|
|
if (db->type != TYPE_DM9000E) {
|
|
|
|
if (int_status & ISR_LNKCHNG) {
|
|
|
|
/* fire a link-change request */
|
|
|
|
schedule_delayed_work(&db->phy_poll, 1);
|
2008-02-04 17:02:17 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-06-24 15:16:02 -06:00
|
|
|
/* Re-enable interrupt mask */
|
|
|
|
iow(db, DM9000_IMR, db->imr_all);
|
|
|
|
|
|
|
|
/* Restore previous register address */
|
|
|
|
writeb(reg_save, db->io_addr);
|
|
|
|
|
2009-03-22 22:28:39 -06:00
|
|
|
spin_unlock_irqrestore(&db->lock, flags);
|
2008-06-24 15:16:02 -06:00
|
|
|
|
|
|
|
return IRQ_HANDLED;
|
2008-02-04 17:02:17 -07:00
|
|
|
}
|
|
|
|
|
2009-11-10 00:22:24 -07:00
|
|
|
static irqreturn_t dm9000_wol_interrupt(int irq, void *dev_id)
|
|
|
|
{
|
|
|
|
struct net_device *dev = dev_id;
|
|
|
|
board_info_t *db = netdev_priv(dev);
|
|
|
|
unsigned long flags;
|
|
|
|
unsigned nsr, wcr;
|
|
|
|
|
|
|
|
spin_lock_irqsave(&db->lock, flags);
|
|
|
|
|
|
|
|
nsr = ior(db, DM9000_NSR);
|
|
|
|
wcr = ior(db, DM9000_WCR);
|
|
|
|
|
|
|
|
dev_dbg(db->dev, "%s: NSR=0x%02x, WCR=0x%02x\n", __func__, nsr, wcr);
|
|
|
|
|
|
|
|
if (nsr & NSR_WAKEST) {
|
|
|
|
/* clear, so we can avoid */
|
|
|
|
iow(db, DM9000_NSR, NSR_WAKEST);
|
|
|
|
|
|
|
|
if (wcr & WCR_LINKST)
|
|
|
|
dev_info(db->dev, "wake by link status change\n");
|
|
|
|
if (wcr & WCR_SAMPLEST)
|
|
|
|
dev_info(db->dev, "wake by sample packet\n");
|
|
|
|
if (wcr & WCR_MAGICST )
|
|
|
|
dev_info(db->dev, "wake by magic packet\n");
|
|
|
|
if (!(wcr & (WCR_LINKST | WCR_SAMPLEST | WCR_MAGICST)))
|
|
|
|
dev_err(db->dev, "wake signalled with no reason? "
|
|
|
|
"NSR=0x%02x, WSR=0x%02x\n", nsr, wcr);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
spin_unlock_irqrestore(&db->lock, flags);
|
|
|
|
|
|
|
|
return (nsr & NSR_WAKEST) ? IRQ_HANDLED : IRQ_NONE;
|
|
|
|
}
|
|
|
|
|
2008-06-24 15:16:02 -06:00
|
|
|
#ifdef CONFIG_NET_POLL_CONTROLLER
|
2005-05-05 16:14:15 -06:00
|
|
|
/*
|
2008-06-24 15:16:02 -06:00
|
|
|
*Used by netconsole
|
2005-05-05 16:14:15 -06:00
|
|
|
*/
|
2008-06-24 15:16:02 -06:00
|
|
|
static void dm9000_poll_controller(struct net_device *dev)
|
2005-05-05 16:14:15 -06:00
|
|
|
{
|
2008-06-24 15:16:02 -06:00
|
|
|
disable_irq(dev->irq);
|
|
|
|
dm9000_interrupt(dev->irq, dev);
|
|
|
|
enable_irq(dev->irq);
|
|
|
|
}
|
|
|
|
#endif
|
2008-02-04 17:02:10 -07:00
|
|
|
|
2008-06-24 15:16:02 -06:00
|
|
|
/*
|
|
|
|
* Open the interface.
|
|
|
|
* The interface is opened whenever "ifconfig" actives it.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
dm9000_open(struct net_device *dev)
|
|
|
|
{
|
2008-11-13 00:38:14 -07:00
|
|
|
board_info_t *db = netdev_priv(dev);
|
2008-06-24 15:16:02 -06:00
|
|
|
unsigned long irqflags = db->irq_res->flags & IRQF_TRIGGER_MASK;
|
2008-02-04 17:02:14 -07:00
|
|
|
|
2008-06-24 15:16:02 -06:00
|
|
|
if (netif_msg_ifup(db))
|
|
|
|
dev_dbg(db->dev, "enabling %s\n", dev->name);
|
2008-02-04 17:02:14 -07:00
|
|
|
|
2008-06-24 15:16:02 -06:00
|
|
|
/* If there is no IRQ type specified, default to something that
|
|
|
|
* may work, and tell the user that this is a problem */
|
2008-02-04 17:02:14 -07:00
|
|
|
|
2008-06-24 15:16:07 -06:00
|
|
|
if (irqflags == IRQF_TRIGGER_NONE)
|
2008-06-24 15:16:02 -06:00
|
|
|
dev_warn(db->dev, "WARNING: no IRQ resource flags set.\n");
|
2008-06-24 15:16:07 -06:00
|
|
|
|
2008-06-24 15:16:02 -06:00
|
|
|
irqflags |= IRQF_SHARED;
|
2008-02-04 17:02:17 -07:00
|
|
|
|
2009-11-19 00:29:17 -07:00
|
|
|
if (request_irq(dev->irq, dm9000_interrupt, irqflags, dev->name, dev))
|
2008-06-24 15:16:02 -06:00
|
|
|
return -EAGAIN;
|
2008-02-04 17:02:14 -07:00
|
|
|
|
2008-06-24 15:16:02 -06:00
|
|
|
/* Initialize DM9000 board */
|
|
|
|
dm9000_reset(db);
|
|
|
|
dm9000_init_dm9000(dev);
|
2008-02-04 17:02:14 -07:00
|
|
|
|
2008-06-24 15:16:02 -06:00
|
|
|
/* Init driver variable */
|
|
|
|
db->dbug_cnt = 0;
|
2008-02-04 17:02:09 -07:00
|
|
|
|
2008-06-24 15:16:02 -06:00
|
|
|
mii_check_media(&db->mii, netif_msg_link(db), 1);
|
|
|
|
netif_start_queue(dev);
|
|
|
|
|
|
|
|
dm9000_schedule_poll(db);
|
2008-02-04 17:02:10 -07:00
|
|
|
|
2008-06-24 15:16:02 -06:00
|
|
|
return 0;
|
|
|
|
}
|
2008-02-04 17:02:14 -07:00
|
|
|
|
2008-06-24 15:16:02 -06:00
|
|
|
/*
|
|
|
|
* Sleep, either by using msleep() or if we are suspending, then
|
|
|
|
* use mdelay() to sleep.
|
|
|
|
*/
|
|
|
|
static void dm9000_msleep(board_info_t *db, unsigned int ms)
|
|
|
|
{
|
|
|
|
if (db->in_suspend)
|
|
|
|
mdelay(ms);
|
|
|
|
else
|
|
|
|
msleep(ms);
|
2005-05-05 16:14:15 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2008-06-24 15:16:02 -06:00
|
|
|
* Read a word from phyxcer
|
2005-05-05 16:14:15 -06:00
|
|
|
*/
|
2008-06-24 15:16:02 -06:00
|
|
|
static int
|
|
|
|
dm9000_phy_read(struct net_device *dev, int phy_reg_unused, int reg)
|
2005-05-05 16:14:15 -06:00
|
|
|
{
|
2008-11-13 00:38:14 -07:00
|
|
|
board_info_t *db = netdev_priv(dev);
|
2008-02-04 17:02:14 -07:00
|
|
|
unsigned long flags;
|
2008-06-24 15:16:02 -06:00
|
|
|
unsigned int reg_save;
|
|
|
|
int ret;
|
2008-02-04 17:02:20 -07:00
|
|
|
|
2008-02-04 17:02:10 -07:00
|
|
|
mutex_lock(&db->addr_lock);
|
|
|
|
|
2008-06-24 15:16:02 -06:00
|
|
|
spin_lock_irqsave(&db->lock,flags);
|
2008-02-04 17:02:14 -07:00
|
|
|
|
2008-06-24 15:16:02 -06:00
|
|
|
/* Save previous register address */
|
|
|
|
reg_save = readb(db->io_addr);
|
2008-02-04 17:02:17 -07:00
|
|
|
|
2008-06-24 15:16:02 -06:00
|
|
|
/* Fill the phyxcer register into REG_0C */
|
|
|
|
iow(db, DM9000_EPAR, DM9000_PHY | reg);
|
2008-02-04 17:02:14 -07:00
|
|
|
|
2008-07-17 13:29:13 -06:00
|
|
|
iow(db, DM9000_EPCR, EPCR_ERPRR | EPCR_EPOS); /* Issue phyxcer read command */
|
2008-02-04 17:02:10 -07:00
|
|
|
|
2008-06-24 15:16:02 -06:00
|
|
|
writeb(reg_save, db->io_addr);
|
|
|
|
spin_unlock_irqrestore(&db->lock,flags);
|
2008-02-04 17:02:07 -07:00
|
|
|
|
2008-02-04 17:02:08 -07:00
|
|
|
dm9000_msleep(db, 1); /* Wait read complete */
|
2008-02-04 17:02:07 -07:00
|
|
|
|
|
|
|
spin_lock_irqsave(&db->lock,flags);
|
|
|
|
reg_save = readb(db->io_addr);
|
|
|
|
|
2005-05-05 16:14:15 -06:00
|
|
|
iow(db, DM9000_EPCR, 0x0); /* Clear phyxcer read command */
|
|
|
|
|
|
|
|
/* The read data keeps on REG_0D & REG_0E */
|
|
|
|
ret = (ior(db, DM9000_EPDRH) << 8) | ior(db, DM9000_EPDRL);
|
|
|
|
|
2005-07-23 10:25:18 -06:00
|
|
|
/* restore the previous address */
|
|
|
|
writeb(reg_save, db->io_addr);
|
2005-05-05 16:14:15 -06:00
|
|
|
spin_unlock_irqrestore(&db->lock,flags);
|
|
|
|
|
2008-02-04 17:02:10 -07:00
|
|
|
mutex_unlock(&db->addr_lock);
|
2008-05-08 04:35:13 -06:00
|
|
|
|
|
|
|
dm9000_dbg(db, 5, "phy_read[%02x] -> %04x\n", reg, ret);
|
2005-05-05 16:14:15 -06:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Write a word to phyxcer
|
|
|
|
*/
|
|
|
|
static void
|
2008-06-24 15:16:01 -06:00
|
|
|
dm9000_phy_write(struct net_device *dev,
|
|
|
|
int phyaddr_unused, int reg, int value)
|
2005-05-05 16:14:15 -06:00
|
|
|
{
|
2008-11-13 00:38:14 -07:00
|
|
|
board_info_t *db = netdev_priv(dev);
|
2005-05-05 16:14:15 -06:00
|
|
|
unsigned long flags;
|
2005-07-23 10:25:18 -06:00
|
|
|
unsigned long reg_save;
|
2005-05-05 16:14:15 -06:00
|
|
|
|
2008-05-08 04:35:13 -06:00
|
|
|
dm9000_dbg(db, 5, "phy_write[%02x] = %04x\n", reg, value);
|
2008-02-04 17:02:10 -07:00
|
|
|
mutex_lock(&db->addr_lock);
|
|
|
|
|
2005-05-05 16:14:15 -06:00
|
|
|
spin_lock_irqsave(&db->lock,flags);
|
|
|
|
|
2005-07-23 10:25:18 -06:00
|
|
|
/* Save previous register address */
|
|
|
|
reg_save = readb(db->io_addr);
|
|
|
|
|
2005-05-05 16:14:15 -06:00
|
|
|
/* Fill the phyxcer register into REG_0C */
|
|
|
|
iow(db, DM9000_EPAR, DM9000_PHY | reg);
|
|
|
|
|
|
|
|
/* Fill the written data into REG_0D & REG_0E */
|
2008-02-04 17:02:19 -07:00
|
|
|
iow(db, DM9000_EPDRL, value);
|
|
|
|
iow(db, DM9000_EPDRH, value >> 8);
|
2005-05-05 16:14:15 -06:00
|
|
|
|
2008-07-17 13:29:13 -06:00
|
|
|
iow(db, DM9000_EPCR, EPCR_EPOS | EPCR_ERPRW); /* Issue phyxcer write command */
|
2008-02-04 17:02:07 -07:00
|
|
|
|
|
|
|
writeb(reg_save, db->io_addr);
|
2008-02-04 17:02:10 -07:00
|
|
|
spin_unlock_irqrestore(&db->lock, flags);
|
2008-02-04 17:02:07 -07:00
|
|
|
|
2008-02-04 17:02:08 -07:00
|
|
|
dm9000_msleep(db, 1); /* Wait write complete */
|
2008-02-04 17:02:07 -07:00
|
|
|
|
|
|
|
spin_lock_irqsave(&db->lock,flags);
|
|
|
|
reg_save = readb(db->io_addr);
|
|
|
|
|
2005-05-05 16:14:15 -06:00
|
|
|
iow(db, DM9000_EPCR, 0x0); /* Clear phyxcer write command */
|
|
|
|
|
2005-07-23 10:25:18 -06:00
|
|
|
/* restore the previous address */
|
|
|
|
writeb(reg_save, db->io_addr);
|
|
|
|
|
2008-02-04 17:02:10 -07:00
|
|
|
spin_unlock_irqrestore(&db->lock, flags);
|
|
|
|
mutex_unlock(&db->addr_lock);
|
2005-05-05 16:14:15 -06:00
|
|
|
}
|
|
|
|
|
2008-06-24 15:16:02 -06:00
|
|
|
static void
|
|
|
|
dm9000_shutdown(struct net_device *dev)
|
|
|
|
{
|
2008-11-13 00:38:14 -07:00
|
|
|
board_info_t *db = netdev_priv(dev);
|
2008-06-24 15:16:02 -06:00
|
|
|
|
|
|
|
/* RESET device */
|
|
|
|
dm9000_phy_write(dev, 0, MII_BMCR, BMCR_RESET); /* PHY RESET */
|
|
|
|
iow(db, DM9000_GPR, 0x01); /* Power-Down PHY */
|
|
|
|
iow(db, DM9000_IMR, IMR_PAR); /* Disable all interrupt */
|
|
|
|
iow(db, DM9000_RCR, 0x00); /* Disable RX */
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Stop the interface.
|
|
|
|
* The interface is stopped when it is brought.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
dm9000_stop(struct net_device *ndev)
|
|
|
|
{
|
2008-11-13 00:38:14 -07:00
|
|
|
board_info_t *db = netdev_priv(ndev);
|
2008-06-24 15:16:02 -06:00
|
|
|
|
|
|
|
if (netif_msg_ifdown(db))
|
|
|
|
dev_dbg(db->dev, "shutting down %s\n", ndev->name);
|
|
|
|
|
|
|
|
cancel_delayed_work_sync(&db->phy_poll);
|
|
|
|
|
|
|
|
netif_stop_queue(ndev);
|
|
|
|
netif_carrier_off(ndev);
|
|
|
|
|
|
|
|
/* free interrupt */
|
|
|
|
free_irq(ndev->irq, ndev);
|
|
|
|
|
|
|
|
dm9000_shutdown(ndev);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-04-15 06:52:37 -06:00
|
|
|
static const struct net_device_ops dm9000_netdev_ops = {
|
|
|
|
.ndo_open = dm9000_open,
|
|
|
|
.ndo_stop = dm9000_stop,
|
|
|
|
.ndo_start_xmit = dm9000_start_xmit,
|
|
|
|
.ndo_tx_timeout = dm9000_timeout,
|
|
|
|
.ndo_set_multicast_list = dm9000_hash_table,
|
|
|
|
.ndo_do_ioctl = dm9000_ioctl,
|
|
|
|
.ndo_change_mtu = eth_change_mtu,
|
|
|
|
.ndo_validate_addr = eth_validate_addr,
|
|
|
|
.ndo_set_mac_address = eth_mac_addr,
|
|
|
|
#ifdef CONFIG_NET_POLL_CONTROLLER
|
|
|
|
.ndo_poll_controller = dm9000_poll_controller,
|
|
|
|
#endif
|
|
|
|
};
|
|
|
|
|
2008-06-24 15:16:02 -06:00
|
|
|
/*
|
|
|
|
* Search DM9000 board, allocate space and register it
|
|
|
|
*/
|
|
|
|
static int __devinit
|
|
|
|
dm9000_probe(struct platform_device *pdev)
|
|
|
|
{
|
|
|
|
struct dm9000_plat_data *pdata = pdev->dev.platform_data;
|
|
|
|
struct board_info *db; /* Point a board information structure */
|
|
|
|
struct net_device *ndev;
|
|
|
|
const unsigned char *mac_src;
|
|
|
|
int ret = 0;
|
|
|
|
int iosize;
|
|
|
|
int i;
|
|
|
|
u32 id_val;
|
|
|
|
|
|
|
|
/* Init network device */
|
|
|
|
ndev = alloc_etherdev(sizeof(struct board_info));
|
|
|
|
if (!ndev) {
|
|
|
|
dev_err(&pdev->dev, "could not allocate device.\n");
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
SET_NETDEV_DEV(ndev, &pdev->dev);
|
|
|
|
|
|
|
|
dev_dbg(&pdev->dev, "dm9000_probe()\n");
|
|
|
|
|
|
|
|
/* setup board info structure */
|
2008-11-13 00:38:14 -07:00
|
|
|
db = netdev_priv(ndev);
|
2008-06-24 15:16:02 -06:00
|
|
|
|
|
|
|
db->dev = &pdev->dev;
|
|
|
|
db->ndev = ndev;
|
|
|
|
|
|
|
|
spin_lock_init(&db->lock);
|
|
|
|
mutex_init(&db->addr_lock);
|
|
|
|
|
|
|
|
INIT_DELAYED_WORK(&db->phy_poll, dm9000_poll_work);
|
|
|
|
|
|
|
|
db->addr_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
|
|
db->data_res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
|
|
|
|
db->irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
|
|
|
|
|
|
|
|
if (db->addr_res == NULL || db->data_res == NULL ||
|
|
|
|
db->irq_res == NULL) {
|
|
|
|
dev_err(db->dev, "insufficient resources\n");
|
|
|
|
ret = -ENOENT;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2009-11-10 00:22:24 -07:00
|
|
|
db->irq_wake = platform_get_irq(pdev, 1);
|
|
|
|
if (db->irq_wake >= 0) {
|
|
|
|
dev_dbg(db->dev, "wakeup irq %d\n", db->irq_wake);
|
|
|
|
|
|
|
|
ret = request_irq(db->irq_wake, dm9000_wol_interrupt,
|
|
|
|
IRQF_SHARED, dev_name(db->dev), ndev);
|
|
|
|
if (ret) {
|
|
|
|
dev_err(db->dev, "cannot get wakeup irq (%d)\n", ret);
|
|
|
|
} else {
|
|
|
|
|
|
|
|
/* test to see if irq is really wakeup capable */
|
|
|
|
ret = set_irq_wake(db->irq_wake, 1);
|
|
|
|
if (ret) {
|
|
|
|
dev_err(db->dev, "irq %d cannot set wakeup (%d)\n",
|
|
|
|
db->irq_wake, ret);
|
|
|
|
ret = 0;
|
|
|
|
} else {
|
|
|
|
set_irq_wake(db->irq_wake, 0);
|
|
|
|
db->wake_supported = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-09-08 19:07:43 -06:00
|
|
|
iosize = resource_size(db->addr_res);
|
2008-06-24 15:16:02 -06:00
|
|
|
db->addr_req = request_mem_region(db->addr_res->start, iosize,
|
|
|
|
pdev->name);
|
|
|
|
|
|
|
|
if (db->addr_req == NULL) {
|
|
|
|
dev_err(db->dev, "cannot claim address reg area\n");
|
|
|
|
ret = -EIO;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
db->io_addr = ioremap(db->addr_res->start, iosize);
|
|
|
|
|
|
|
|
if (db->io_addr == NULL) {
|
|
|
|
dev_err(db->dev, "failed to ioremap address reg\n");
|
|
|
|
ret = -EINVAL;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2009-09-08 19:07:43 -06:00
|
|
|
iosize = resource_size(db->data_res);
|
2008-06-24 15:16:02 -06:00
|
|
|
db->data_req = request_mem_region(db->data_res->start, iosize,
|
|
|
|
pdev->name);
|
|
|
|
|
|
|
|
if (db->data_req == NULL) {
|
|
|
|
dev_err(db->dev, "cannot claim data reg area\n");
|
|
|
|
ret = -EIO;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
db->io_data = ioremap(db->data_res->start, iosize);
|
|
|
|
|
|
|
|
if (db->io_data == NULL) {
|
|
|
|
dev_err(db->dev, "failed to ioremap data reg\n");
|
|
|
|
ret = -EINVAL;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* fill in parameters for net-dev structure */
|
|
|
|
ndev->base_addr = (unsigned long)db->io_addr;
|
|
|
|
ndev->irq = db->irq_res->start;
|
|
|
|
|
|
|
|
/* ensure at least we have a default set of IO routines */
|
|
|
|
dm9000_set_io(db, iosize);
|
|
|
|
|
|
|
|
/* check to see if anything is being over-ridden */
|
|
|
|
if (pdata != NULL) {
|
|
|
|
/* check to see if the driver wants to over-ride the
|
|
|
|
* default IO width */
|
|
|
|
|
|
|
|
if (pdata->flags & DM9000_PLATF_8BITONLY)
|
|
|
|
dm9000_set_io(db, 1);
|
|
|
|
|
|
|
|
if (pdata->flags & DM9000_PLATF_16BITONLY)
|
|
|
|
dm9000_set_io(db, 2);
|
|
|
|
|
|
|
|
if (pdata->flags & DM9000_PLATF_32BITONLY)
|
|
|
|
dm9000_set_io(db, 4);
|
|
|
|
|
|
|
|
/* check to see if there are any IO routine
|
|
|
|
* over-rides */
|
|
|
|
|
|
|
|
if (pdata->inblk != NULL)
|
|
|
|
db->inblk = pdata->inblk;
|
|
|
|
|
|
|
|
if (pdata->outblk != NULL)
|
|
|
|
db->outblk = pdata->outblk;
|
|
|
|
|
|
|
|
if (pdata->dumpblk != NULL)
|
|
|
|
db->dumpblk = pdata->dumpblk;
|
|
|
|
|
|
|
|
db->flags = pdata->flags;
|
|
|
|
}
|
|
|
|
|
2008-06-24 15:16:04 -06:00
|
|
|
#ifdef CONFIG_DM9000_FORCE_SIMPLE_PHY_POLL
|
|
|
|
db->flags |= DM9000_PLATF_SIMPLE_PHY;
|
|
|
|
#endif
|
|
|
|
|
2008-06-24 15:16:02 -06:00
|
|
|
dm9000_reset(db);
|
|
|
|
|
|
|
|
/* try multiple times, DM9000 sometimes gets the read wrong */
|
|
|
|
for (i = 0; i < 8; i++) {
|
|
|
|
id_val = ior(db, DM9000_VIDL);
|
|
|
|
id_val |= (u32)ior(db, DM9000_VIDH) << 8;
|
|
|
|
id_val |= (u32)ior(db, DM9000_PIDL) << 16;
|
|
|
|
id_val |= (u32)ior(db, DM9000_PIDH) << 24;
|
|
|
|
|
|
|
|
if (id_val == DM9000_ID)
|
|
|
|
break;
|
|
|
|
dev_err(db->dev, "read wrong id 0x%08x\n", id_val);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (id_val != DM9000_ID) {
|
|
|
|
dev_err(db->dev, "wrong id: 0x%08x\n", id_val);
|
|
|
|
ret = -ENODEV;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Identify what type of DM9000 we are working on */
|
|
|
|
|
|
|
|
id_val = ior(db, DM9000_CHIPR);
|
|
|
|
dev_dbg(db->dev, "dm9000 revision 0x%02x\n", id_val);
|
|
|
|
|
|
|
|
switch (id_val) {
|
|
|
|
case CHIPR_DM9000A:
|
|
|
|
db->type = TYPE_DM9000A;
|
|
|
|
break;
|
|
|
|
case CHIPR_DM9000B:
|
|
|
|
db->type = TYPE_DM9000B;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
dev_dbg(db->dev, "ID %02x => defaulting to DM9000E\n", id_val);
|
|
|
|
db->type = TYPE_DM9000E;
|
|
|
|
}
|
|
|
|
|
2009-07-06 19:12:33 -06:00
|
|
|
/* dm9000a/b are capable of hardware checksum offload */
|
|
|
|
if (db->type == TYPE_DM9000A || db->type == TYPE_DM9000B) {
|
|
|
|
db->can_csum = 1;
|
|
|
|
db->rx_csum = 1;
|
|
|
|
ndev->features |= NETIF_F_IP_CSUM;
|
|
|
|
}
|
|
|
|
|
2008-06-24 15:16:02 -06:00
|
|
|
/* from this point we assume that we have found a DM9000 */
|
|
|
|
|
|
|
|
/* driver system function */
|
|
|
|
ether_setup(ndev);
|
|
|
|
|
2009-04-15 06:52:37 -06:00
|
|
|
ndev->netdev_ops = &dm9000_netdev_ops;
|
|
|
|
ndev->watchdog_timeo = msecs_to_jiffies(watchdog);
|
|
|
|
ndev->ethtool_ops = &dm9000_ethtool_ops;
|
2008-06-24 15:16:02 -06:00
|
|
|
|
|
|
|
db->msg_enable = NETIF_MSG_LINK;
|
|
|
|
db->mii.phy_id_mask = 0x1f;
|
|
|
|
db->mii.reg_num_mask = 0x1f;
|
|
|
|
db->mii.force_media = 0;
|
|
|
|
db->mii.full_duplex = 0;
|
|
|
|
db->mii.dev = ndev;
|
|
|
|
db->mii.mdio_read = dm9000_phy_read;
|
|
|
|
db->mii.mdio_write = dm9000_phy_write;
|
|
|
|
|
|
|
|
mac_src = "eeprom";
|
|
|
|
|
|
|
|
/* try reading the node address from the attached EEPROM */
|
|
|
|
for (i = 0; i < 6; i += 2)
|
|
|
|
dm9000_read_eeprom(db, i / 2, ndev->dev_addr+i);
|
|
|
|
|
2008-07-23 09:41:52 -06:00
|
|
|
if (!is_valid_ether_addr(ndev->dev_addr) && pdata != NULL) {
|
|
|
|
mac_src = "platform data";
|
|
|
|
memcpy(ndev->dev_addr, pdata->dev_addr, 6);
|
|
|
|
}
|
|
|
|
|
2008-06-24 15:16:02 -06:00
|
|
|
if (!is_valid_ether_addr(ndev->dev_addr)) {
|
|
|
|
/* try reading from mac */
|
|
|
|
|
|
|
|
mac_src = "chip";
|
|
|
|
for (i = 0; i < 6; i++)
|
|
|
|
ndev->dev_addr[i] = ior(db, i+DM9000_PAR);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!is_valid_ether_addr(ndev->dev_addr))
|
|
|
|
dev_warn(db->dev, "%s: Invalid ethernet MAC address. Please "
|
|
|
|
"set using ifconfig\n", ndev->name);
|
|
|
|
|
|
|
|
platform_set_drvdata(pdev, ndev);
|
|
|
|
ret = register_netdev(ndev);
|
|
|
|
|
2008-10-27 16:59:26 -06:00
|
|
|
if (ret == 0)
|
|
|
|
printk(KERN_INFO "%s: dm9000%c at %p,%p IRQ %d MAC: %pM (%s)\n",
|
2008-06-24 15:16:02 -06:00
|
|
|
ndev->name, dm9000_type_to_char(db->type),
|
|
|
|
db->io_addr, db->io_data, ndev->irq,
|
2008-10-27 16:59:26 -06:00
|
|
|
ndev->dev_addr, mac_src);
|
2008-06-24 15:16:02 -06:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
out:
|
|
|
|
dev_err(db->dev, "not found (%d).\n", ret);
|
|
|
|
|
|
|
|
dm9000_release_board(pdev, db);
|
|
|
|
free_netdev(ndev);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2005-05-05 16:14:15 -06:00
|
|
|
static int
|
2009-07-21 13:37:18 -06:00
|
|
|
dm9000_drv_suspend(struct device *dev)
|
2005-05-05 16:14:15 -06:00
|
|
|
{
|
2009-07-21 13:37:18 -06:00
|
|
|
struct platform_device *pdev = to_platform_device(dev);
|
|
|
|
struct net_device *ndev = platform_get_drvdata(pdev);
|
2008-02-04 17:02:08 -07:00
|
|
|
board_info_t *db;
|
2005-05-05 16:14:15 -06:00
|
|
|
|
2005-10-28 10:52:56 -06:00
|
|
|
if (ndev) {
|
2008-11-13 00:38:14 -07:00
|
|
|
db = netdev_priv(ndev);
|
2008-02-04 17:02:08 -07:00
|
|
|
db->in_suspend = 1;
|
|
|
|
|
2009-11-10 00:22:24 -07:00
|
|
|
if (!netif_running(ndev))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
netif_device_detach(ndev);
|
|
|
|
|
|
|
|
/* only shutdown if not using WoL */
|
|
|
|
if (!db->wake_state)
|
2005-05-05 16:14:15 -06:00
|
|
|
dm9000_shutdown(ndev);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2009-07-21 13:37:18 -06:00
|
|
|
dm9000_drv_resume(struct device *dev)
|
2005-05-05 16:14:15 -06:00
|
|
|
{
|
2009-07-21 13:37:18 -06:00
|
|
|
struct platform_device *pdev = to_platform_device(dev);
|
|
|
|
struct net_device *ndev = platform_get_drvdata(pdev);
|
2008-11-13 00:38:14 -07:00
|
|
|
board_info_t *db = netdev_priv(ndev);
|
2005-05-05 16:14:15 -06:00
|
|
|
|
2005-10-28 10:52:56 -06:00
|
|
|
if (ndev) {
|
2005-05-05 16:14:15 -06:00
|
|
|
if (netif_running(ndev)) {
|
2009-11-10 00:22:24 -07:00
|
|
|
/* reset if we were not in wake mode to ensure if
|
|
|
|
* the device was powered off it is in a known state */
|
|
|
|
if (!db->wake_state) {
|
|
|
|
dm9000_reset(db);
|
|
|
|
dm9000_init_dm9000(ndev);
|
|
|
|
}
|
2005-05-05 16:14:15 -06:00
|
|
|
|
|
|
|
netif_device_attach(ndev);
|
|
|
|
}
|
2008-02-04 17:02:08 -07:00
|
|
|
|
|
|
|
db->in_suspend = 0;
|
2005-05-05 16:14:15 -06:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-12-14 19:00:08 -07:00
|
|
|
static const struct dev_pm_ops dm9000_drv_pm_ops = {
|
2009-07-21 13:37:18 -06:00
|
|
|
.suspend = dm9000_drv_suspend,
|
|
|
|
.resume = dm9000_drv_resume,
|
|
|
|
};
|
|
|
|
|
2008-05-08 04:33:03 -06:00
|
|
|
static int __devexit
|
2005-11-09 15:32:44 -07:00
|
|
|
dm9000_drv_remove(struct platform_device *pdev)
|
2005-05-05 16:14:15 -06:00
|
|
|
{
|
2005-11-09 15:32:44 -07:00
|
|
|
struct net_device *ndev = platform_get_drvdata(pdev);
|
2005-05-05 16:14:15 -06:00
|
|
|
|
2005-11-09 15:32:44 -07:00
|
|
|
platform_set_drvdata(pdev, NULL);
|
2005-05-05 16:14:15 -06:00
|
|
|
|
|
|
|
unregister_netdev(ndev);
|
2008-11-16 13:41:35 -07:00
|
|
|
dm9000_release_board(pdev, (board_info_t *) netdev_priv(ndev));
|
2007-05-07 05:13:25 -06:00
|
|
|
free_netdev(ndev); /* free device structure */
|
2005-05-05 16:14:15 -06:00
|
|
|
|
2008-02-04 17:02:02 -07:00
|
|
|
dev_dbg(&pdev->dev, "released and freed device\n");
|
2005-05-05 16:14:15 -06:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-11-09 15:32:44 -07:00
|
|
|
static struct platform_driver dm9000_driver = {
|
2006-06-13 17:09:17 -06:00
|
|
|
.driver = {
|
|
|
|
.name = "dm9000",
|
|
|
|
.owner = THIS_MODULE,
|
2009-07-21 13:37:18 -06:00
|
|
|
.pm = &dm9000_drv_pm_ops,
|
2006-06-13 17:09:17 -06:00
|
|
|
},
|
2005-05-05 16:14:15 -06:00
|
|
|
.probe = dm9000_probe,
|
2008-05-08 04:33:03 -06:00
|
|
|
.remove = __devexit_p(dm9000_drv_remove),
|
2005-05-05 16:14:15 -06:00
|
|
|
};
|
|
|
|
|
|
|
|
static int __init
|
|
|
|
dm9000_init(void)
|
|
|
|
{
|
2008-02-04 17:02:06 -07:00
|
|
|
printk(KERN_INFO "%s Ethernet Driver, V%s\n", CARDNAME, DRV_VERSION);
|
2005-07-23 10:29:38 -06:00
|
|
|
|
2008-06-24 15:16:01 -06:00
|
|
|
return platform_driver_register(&dm9000_driver);
|
2005-05-05 16:14:15 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
static void __exit
|
|
|
|
dm9000_cleanup(void)
|
|
|
|
{
|
2005-11-09 15:32:44 -07:00
|
|
|
platform_driver_unregister(&dm9000_driver);
|
2005-05-05 16:14:15 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
module_init(dm9000_init);
|
|
|
|
module_exit(dm9000_cleanup);
|
|
|
|
|
|
|
|
MODULE_AUTHOR("Sascha Hauer, Ben Dooks");
|
|
|
|
MODULE_DESCRIPTION("Davicom DM9000 network driver");
|
|
|
|
MODULE_LICENSE("GPL");
|
2008-04-18 14:50:44 -06:00
|
|
|
MODULE_ALIAS("platform:dm9000");
|