/*
* cs8900a.c: A Crystal Semiconductor (Now Cirrus Logic) CS8900A
driver for SMDK-s3c2410 (based on cs89x0.c)
*
* Author: Yong-iL Joh <tolkien@mizi.com>
* Date : $Date: 2002/10/16 09:08:07 $
*
* $Revision: 1.1.2.5 $
Wed Aug 14 2002 Yong-iL Joh <tolkien@mizi.com>
- initial, based on cs89x0.c
Wed Aug 16 2002 Yong-iL Joh <tolkien@mizi.com>
- working!
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file COPYING in the main directory of this archive
* for more details.
*/
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/spinlock.h>
#include <linux/ioport.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/hardware.h>
#define IRQ_LAN IRQ_CS8900
#include "cs89x0.h"
/*
* Set this to zero to remove all the debug statements via
* dead code elimination
*/
#undef DEBUGGING 4
#if DEBUGGING
#define DPRINTK(n, args...) \
if (n <= DEBUGGING) { \
printk(args); \
}
#else
#define DPRINTK(n, args...)
#endif
#if DEBUGGING
static char version[] __initdata =
"cs89x0.c: v2.4.3-pre1 Russell Nelson <nelson@crynwr.com>, Andrew Morton <andrewm@uow.edu.au>\n";
#endif
/* First, a few definitions that the brave might change.
A zero-terminated list of I/O addresses to be probed. Some special flags..
Addr & 1 = Read back the address port, look for signature and reset
the page window before probing
Addr & 3 = Reset the page window and probe
The CLPS eval board has the Cirrus chip at 0x80090300, in ARM IO space,
but it is possible that a Cirrus board could be plugged into the ISA
slots. */
static unsigned int netcard_portlist[] __initdata =
{ vCS8900_BASE + 0x300, 0};
/* The number of low I/O ports used by the ethercard. */
#define NETCARD_IO_EXTENT 0xfff
/* we allow the user to override various values normally set in the EEPROM */
#define FORCE_RJ45 0x0001 /* pick one of these three */
#define FORCE_AUI 0x0002
#define FORCE_BNC 0x0004
#define FORCE_AUTO 0x0010 /* pick one of these three */
#define FORCE_HALF 0x0020
#define FORCE_FULL 0x0030
/* Information that need to be kept for each board. */
struct net_local {
struct net_device_stats stats;
int chip_type; /* one of: CS8900, CS8920, CS8920M */
char chip_revision; /* revision letter of the chip ('A'...) */
int send_cmd; /* the proper send command:
TX_NOW, TX_AFTER_381, or TX_AFTER_ALL */
int auto_neg_cnf; /* auto-negotiation word from EEPROM */
int adapter_cnf; /* adapter configuration from EEPROM */
int isa_config; /* ISA configuration from EEPROM */
int irq_map; /* IRQ map from EEPROM */
int rx_mode; /* what mode are we in?
0, RX_MULTCAST_ACCEPT, or RX_ALL_ACCEPT */
int curr_rx_cfg; /* a copy of PP_RxCFG */
int linectl; /* either 0 or LOW_RX_SQUELCH,
depending on configuration. */
int send_underrun; /* keep track of how many underruns
in a row we get */
int force; /* force various values; see FORCE* above. */
spinlock_t lock;
};
/* Index to functions, as function prototypes. */
extern int cs89x0_probe(struct net_device *dev);
static int cs89x0_probe1(struct net_device *dev, int ioaddr);
static int net_open(struct net_device *dev);
static int net_send_packet(struct sk_buff *skb, struct net_device *dev);
static void net_interrupt(int irq, void *dev_id, struct pt_regs *regs);
static void set_multicast_list(struct net_device *dev);
static void net_timeout(struct net_device *dev);
static void net_rx(struct net_device *dev);
static int net_close(struct net_device *dev);
static struct net_device_stats *net_get_stats(struct net_device *dev);
static void reset_chip(struct net_device *dev);
static int set_mac_address(struct net_device *dev, void *addr);
static void count_rx_errors(int status, struct net_local *lp);
/* Example routines you must write ;->. */
#define tx_done(dev) 1
/* Check for a network adaptor of this type, and return '0' iff one exists.
If dev->base_addr == 0, probe all likely locations.
If dev->base_addr == 1, always return failure.
If dev->base_addr == 2, allocate space for the device and return success
(detachable devices only).
Return 0 on success.
*/
int __init cs89x0_probe(struct net_device *dev) {
int i;
SET_MODULE_OWNER(dev);
DPRINTK(1, "cs89x0:cs89x0_probe(0x%x)\n", base_addr);
BWSCON = (BWSCON & ~(BWSCON_ST3 | BWSCON_WS3 | BWSCON_DW3)) |
(BWSCON_ST3 | BWSCON_WS3 | BWSCON_DW(3, BWSCON_DW_16));
BANKCON3= BANKCON_Tacs0 | BANKCON_Tcos4 | BANKCON_Tacc14 |
BANKCON_Toch1 | BANKCON_Tcah4 | BANKCON_Tacp6 | BANKCON_PMC1;
set_external_irq(IRQ_CS8900, EXT_RISING_EDGE, GPIO_PULLUP_DIS);
for (i = 0; netcard_portlist[i]; i++) {
if (cs89x0_probe1(dev, netcard_portlist[i]) == 0)
return 0;
}
printk(KERN_WARNING "cs89x0: no cs8900 or cs8920 detected."
"Be sure to disable PnP with SETUP\n");
return -ENODEV;
}
inline int readreg(struct net_device *dev, int portno) {
outw(portno, dev->base_addr + ADD_PORT);
return inw(dev->base_addr + DATA_PORT);
}
inline void writereg(struct net_device *dev, int portno, int value) {
outw(portno, dev->base_addr + ADD_PORT);
outw(value, dev->base_addr + DATA_PORT);
}
inline int readword(struct net_device *dev, int portno) {
return inw(dev->base_addr + portno);
}
inline void writeword(struct net_device *dev, int portno, int value) {
outw(value, dev->base_addr + portno);
}
inline void writeblock(struct net_device *dev, char *pData, int Length) {
int i;
for (i = 0 ; i < (Length/2); i++) {
writeword(dev, TX_FRAME_PORT, *(u16 *)pData );
pData += 2;
}
if (Length % 2) {
u16 OddWordValue = *pData;
writeword(dev, TX_FRAME_PORT, OddWordValue);
}
}
inline void readblock(struct net_device *dev, char *pData, int Length) {
u16 InputWord;
int i;
for (i=0; i < (Length/2); i++) {
InputWord = readword(dev, RX_FRAME_PORT);
*(u8*)pData++ = (u8) InputWord & 0xFF;
*(u8*)pData++ = (u8) (InputWord >> 8) & 0xFF;
}
if (Length & 0x1)
*pData = (u8) (readword(dev, RX_FRAME_PORT) & 0xff);
}
/* This is the real probe routine. Linux has a history of friendly device
probes on the ISA bus. A good device probes avoids doing writes, and
verifies that the correct device exists and functions.
Return 0 on success.
*/
static int __init cs89x0_probe1(struct net_device *dev, int ioaddr) {
struct net_local *lp;
#if DEBUGGING
static unsigned version_printed;
#endif
unsigned rev_type = 0;
int ret;
/* Initialize the device structure. */
if (dev->priv == NULL) {
dev->priv = kmalloc(sizeof(struct net_local), GFP_KERNEL);
if (dev->priv == 0) {
ret = -ENOMEM;
goto before_kmalloc;
}
lp = (struct net_local *)dev->priv;
memset(lp, 0, sizeof(*lp));
spin_lock_init(&lp->lock);
}
lp = (struct net_local *)dev->priv;
/* Fill in the 'dev' fields. */
dev->base_addr = ioaddr;
/* Bus Reset Consideration */
ret = readword(dev, ADD_PORT);
if ((ret & ADD_MASK) != ADD_SIG ) {
DPRINTK(1, __FUNCTION__ " 0x%08X\n", ret);
ret = -ENODEV;
goto after_kmalloc;
}
/* get the chip type */
rev_type = readreg(dev, PRODUCT_ID_ADD);
lp->chip_type = rev_type &~ REVISON_BITS;
lp->chip_revision = ((rev_type & REVISON_BITS) >> 8) + 'A';
#if DEBUGGING
if (version_printed++ == 0)
printk(version);
#endif
printk(KERN_INFO "%s: cs89%c0%s rev %c(%s) found at %#3lx\n",
dev->name,
lp->chip_type==CS8900 ? '0' : '2',
lp->chip_type==CS8920M ? "M" : "",
lp->chip_revision,
readreg(dev, PP_SelfST) & ACTIVE_33V ? "3.3 Volts" : "5 Volts",
dev->base_addr);
if (lp->chip_type != CS8900) {
printk(__