/*
* Copyright 2000, 2001 MontaVista Software Inc.
* Author: MontaVista Software, Inc.
* stevel@mvista.com or source@mvista.com
*
* This program is free software; you can distribute it and/or modify it
* under the terms of the GNU General Public License (Version 2) as
* published by the Free Software Foundation.
*
* This program is distributed in the hope 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.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
*
* Ethernet driver for the MIPS GT96100 Advanced Communication Controller.
*
* Revision history
*
* 11.11.2001 Moved to 2.4.14, ppopov@mvista.com. Modified driver to add
* proper gt96100A support.
* 12.05.2001 Moved eth port 0 to irq 3 (mapped to GT_SERINT0 on EV96100A)
* in order for both ports to work. Also cleaned up boot
* option support (mac address string parsing), fleshed out
* gt96100_cleanup_module(), and other general code cleanups
* <stevel@mvista.com>.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/timer.h>
#include <linux/errno.h>
#include <linux/in.h>
#include <linux/ioport.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/pci.h>
#include <linux/init.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include <linux/delay.h>
#include <linux/ctype.h>
#include <linux/bitops.h>
#include <asm/irq.h>
#include <asm/io.h>
#define DESC_BE 1
#define DESC_DATA_BE 1
#define GT96100_DEBUG 2
#include "gt96100eth.h"
// prototypes
static void* dmaalloc(size_t size, dma_addr_t *dma_handle);
static void dmafree(size_t size, void *vaddr);
static void gt96100_delay(int msec);
static int gt96100_add_hash_entry(struct net_device *dev,
unsigned char* addr);
static void read_mib_counters(struct gt96100_private *gp);
static int read_MII(int phy_addr, u32 reg);
static int write_MII(int phy_addr, u32 reg, u16 data);
static int gt96100_init_module(void);
static void gt96100_cleanup_module(void);
static void dump_MII(int dbg_lvl, struct net_device *dev);
static void dump_tx_desc(int dbg_lvl, struct net_device *dev, int i);
static void dump_rx_desc(int dbg_lvl, struct net_device *dev, int i);
static void dump_skb(int dbg_lvl, struct net_device *dev,
struct sk_buff *skb);
static void update_stats(struct gt96100_private *gp);
static void abort(struct net_device *dev, u32 abort_bits);
static void hard_stop(struct net_device *dev);
static void enable_ether_irq(struct net_device *dev);
static void disable_ether_irq(struct net_device *dev);
static int gt96100_probe1(struct pci_dev *pci, int port_num);
static void reset_tx(struct net_device *dev);
static void reset_rx(struct net_device *dev);
static int gt96100_check_tx_consistent(struct gt96100_private *gp);
static int gt96100_init(struct net_device *dev);
static int gt96100_open(struct net_device *dev);
static int gt96100_close(struct net_device *dev);
static int gt96100_tx(struct sk_buff *skb, struct net_device *dev);
static int gt96100_rx(struct net_device *dev, u32 status);
static irqreturn_t gt96100_interrupt(int irq, void *dev_id, struct pt_regs *regs);
static void gt96100_tx_timeout(struct net_device *dev);
static void gt96100_set_rx_mode(struct net_device *dev);
static struct net_device_stats* gt96100_get_stats(struct net_device *dev);
extern char * __init prom_getcmdline(void);
static int max_interrupt_work = 32;
#define nibswap(x) ((((x) >> 4) & 0x0f) | (((x) << 4) & 0xf0))
#define RUN_AT(x) (jiffies + (x))
// For reading/writing 32-bit words and half-words from/to DMA memory
#ifdef DESC_BE
#define cpu_to_dma32 cpu_to_be32
#define dma32_to_cpu be32_to_cpu
#define cpu_to_dma16 cpu_to_be16
#define dma16_to_cpu be16_to_cpu
#else
#define cpu_to_dma32 cpu_to_le32
#define dma32_to_cpu le32_to_cpu
#define cpu_to_dma16 cpu_to_le16
#define dma16_to_cpu le16_to_cpu
#endif
static char mac0[18] = "00.02.03.04.05.06";
static char mac1[18] = "00.01.02.03.04.05";
module_param_string(mac0, mac0, 18, 0);
module_param_string(mac1, mac0, 18, 0);
MODULE_PARM_DESC(mac0, "MAC address for GT96100 ethernet port 0");
MODULE_PARM_DESC(mac1, "MAC address for GT96100 ethernet port 1");
/*
* Info for the GT96100 ethernet controller's ports.
*/
static struct gt96100_if_t {
struct net_device *dev;
unsigned int iobase; // IO Base address of this port
int irq; // IRQ number of this port
char *mac_str;
} gt96100_iflist[NUM_INTERFACES] = {
{
NULL,
GT96100_ETH0_BASE, GT96100_ETHER0_IRQ,
mac0
},
{
NULL,
GT96100_ETH1_BASE, GT96100_ETHER1_IRQ,
mac1
}
};
static inline const char*
chip_name(int chip_rev)
{
switch (chip_rev) {
case REV_GT96100:
return "GT96100";
case REV_GT96100A_1:
case REV_GT96100A:
return "GT96100A";
default:
return "Unknown GT96100";
}
}
/*
DMA memory allocation, derived from pci_alloc_consistent.
*/
static void * dmaalloc(size_t size, dma_addr_t *dma_handle)
{
void *ret;
ret = (void *)__get_free_pages(GFP_ATOMIC | GFP_DMA, get_order(size));
if (ret != NULL) {
dma_cache_inv((unsigned long)ret, size);
if (dma_handle != NULL)
*dma_handle = virt_to_phys(ret);
/* bump virtual address up to non-cached area */
ret = (void*)KSEG1ADDR(ret);
}
return ret;
}
static void dmafree(size_t size, void *vaddr)
{
vaddr = (void*)KSEG0ADDR(vaddr);
free_pages((unsigned long)vaddr, get_order(size));
}
static void gt96100_delay(int ms)
{
if (in_interrupt())
return;
else
msleep_interruptible(ms);
}
static int
parse_mac_addr(struct net_device *dev, char* macstr)
{
int i, j;
unsigned char result, value;
for (i=0; i<6; i++) {
result = 0;
if (i != 5 && *(macstr+2) != '.') {
err(__FILE__ "invalid mac addres
|