📄 bluecat_cs8900a.c
字号:
/* Created by ISDCorp 1999 www.isdcorp.com */
/* cs8900a.c: A Crystal Semiconductor CS89[02]0 driver for linux on
* EP7312 platform
*
*Based on cs89x0.c
*/
static char *version =
"cs8900.c:v0.01 based on v1.02 Russell Nelson <nelson@crynwr.com>\n";
/* use 0 for production, 1 for verification, >2 for debug */
#ifndef NET_DEBUG
#define NET_DEBUG 6
#endif
#ifdef MODULE
#include <linux/module.h>
#include <linux/version.h>
#else
#define MOD_INC_USE_COUNT
#define MOD_DEC_USE_COUNT
#endif
#define PRINTK(x) printk x
extern intnfsdebugon;
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/types.h>
#include <linux/fcntl.h>
#include <linux/interrupt.h>
#include <linux/ptrace.h>
#include <linux/ioport.h>
#include <linux/in.h>
#include <linux/malloc.h>
#include <linux/string.h>
#include <asm/system.h>
#include <asm/bitops.h>
#include <asm/io.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <asm/arch/irqs.h>
#include <linux/init.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include <asm/arch/hardware.h>
#include "cs8900a.h"
#include <asm/arch/armdebug.h>
/*
* Locally used declarations
*/
#define NETCARD_IO_EXTENT 16
#define tx_done(dev) 1
#define EPORT_ADDR 0x300
#define EIOADDR (EIO_BASE+EPORT_ADDR)
#define __EIOB(x) (*(volatile unsigned char *) (EIOADDR+(x)))
#define __EIOW(x) (*(volatile uint32_t *) (EIOADDR+(x)))
#undef TX_CMD_PORT
#undef TX_LEN_PORT
#undef ISQ_PORT
#undef ADD_PORT
#undef DATA_PORT
#undef DATA32_PORT
#if defined(CONFIG_ARCH_CLEE89712) || defined(CONFIG_ARCH_CLEP7312)
#define TX_CMD_PORT 0x0004
#define TX_LEN_PORT 0x0006
#define ISQ_PORT 0x0008
#define ADD_PORT 0x000A
#define DATA_PORT 0x000C
#define DATA32_PORT 0x000E
#elif defined(CONFIG_ARCH_CLEP7212)
#define TX_CMD_PORT 0x0008
#define TX_LEN_PORT 0x000c
#define ISQ_PORT 0x0010
#define ADD_PORT 0x0014
#define DATA_PORT 0x0018
#define DATA32_PORT 0x001c
#else
#error "Unsupported architecture"
#endif
/* 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 propercommand used to send a packet. */
int auto_neg_cnf;
int adapter_cnf;
int isa_config;
int irq_map;
int rx_mode;
int curr_rx_cfg;
int linectl;
int send_underrun; /* keep track of how many UE we get */
struct sk_buff *skb;
};
extern int cs89x0_probe(struct device *dev);
static int cs89x0_probe1(struct device *dev, int ioaddr);
static int net_open(struct device *dev);
static int net_send_packet(struct sk_buff *skb, struct device *dev);
static void net_interrupt(int irq, void *dev_id, struct pt_regs *regs);
static void set_multicast_list(struct device *dev);
static void net_rx(struct device *dev);
static int net_close(struct device *dev);
static struct net_device_stats *net_get_stats(struct device *dev);
static void reset_chip(struct device *dev);
static int set_mac_address(struct device *dev, void *addr);
static int get_eeprom_data(struct device *dev, int off, int len,int *buffer);
static int get_eeprom_cksum(int off, int len, int *buffer);
static int inline readreg(struct device *dev, int portno);
void cs_set_cnf( struct device *dev, struct net_local *lp );
static void inline writereg(struct device *dev, int portno, int value);
extern void enable_irq(unsigned int irq);
static uint16_t inline readword(struct device *dev, int portno);
void cs_ins(int port, uint16_t *to, int len );
void cs_outs(int port, uint16_t *from, int len );
#ifdef SET_NET_DEBUG
static void printpkt( unsigned char *bptr, int len );
#endif
#ifdef __bluecat__
char macaddr[6] = { 0x00, 0xe0, 0x98, 0x06, 0x91, 0x66 };
static int macaddr_changed = 0;
void cs8900a_setup( char * str, int * ints )
{
int i;
char ch, *pos;
u8 byte = 0;
char addr[6];
for( pos=str, i=0; *pos; pos++ ) {
ch = *pos;
if( ch == ':' ) {
if( i++ < 6 ) {
byte = 0;
continue;
} else {
printk("Bad MAC address: '%s' - ignored.\n", str);
return;
}
} else {
byte <<= 4;
if( (ch >= '0') && (ch <= '9') ) {
byte += ch - '0';
} else if( (ch >= 'a') && (ch <= 'f') ) {
byte += ch - 'a' + 10;
} else if( (ch >= 'A') && (ch <= 'F') ) {
byte += ch - 'F' + 10;
} else {
printk("Bad MAC address: '%s' - ignored.\n", str);
return;
}
addr[i] = byte;
}
}
for (i = 0; i < 6; i++)
macaddr[i] = addr[i];
macaddr_changed = 1;
}
#endif
/*
* Variables used by the driver
*/
static unsigned int net_debug = 0;
__initfunc(int
cs89x0_probe(struct device *dev))
{
int base_addr = dev ? dev->base_addr : 0;
if (base_addr > 0x1ff)/* Check a single specified location. */
return cs89x0_probe1(dev, base_addr);
else if (base_addr != 0)/* Don't probe at all. */
return ENXIO;
dev->base_addr = EPORT_ADDR;
if (cs89x0_probe1(dev, EPORT_ADDR) == 0)
return 0;
printk("cs89x0: no cs8900 or cs8920 detected.\n");
printk("Be sure to disable PnP with SETUP\n");
return ENODEV;
}
/* 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. */
__initfunc(static int cs89x0_probe1(struct device *dev, int ioaddr))
{
struct net_local *lp;
static unsigned version_printed = 0;
int i;
unsigned rev_type = 0;
int eeprom_buff[CHKSUM_LEN];
/* Initialize the device structure. */
if (dev->priv == NULL) {
dev->priv = kmalloc(sizeof(struct net_local), GFP_KERNEL);
memset(dev->priv, 0, sizeof(struct net_local));
}
lp = (struct net_local *)dev->priv;
/* Fill in the 'dev' fields. */
dev->base_addr = EPORT_ADDR;
/* 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';
/* Check the chip type and revision in order to set correct send command
CS8920 revision C and CS8900 revision F can use the faster send. */
lp->send_cmd = TX_AFTER_381;
if (lp->chip_type == CS8900 && lp->chip_revision >= 'F')
lp->send_cmd = TX_NOW;
if (net_debug && version_printed++ == 0)
printk(version);
printk("%s: cs89%c0%s rev %c found at %#3lx",
dev->name,
lp->chip_type==CS8900?'0':'2',
lp->chip_type==CS8920M?"M":"",
lp->chip_revision,
dev->base_addr);
reset_chip(dev);
/* First check to see if an EEPROM is attached*/
if ((readreg(dev, PP_SelfST) & EEPROM_PRESENT) == 0) {
printk("\ncs89x0: No EEPROM, using default setup temporarily.\n");
cs_set_cnf( dev, lp );
}else if (get_eeprom_data(dev,START_EEPROM_DATA,CHKSUM_LEN,eeprom_buff)<0) {
printk("\ncs89x0: EEPROM read failed, using default setup temporarily.\n");
cs_set_cnf( dev, lp );
}else if (get_eeprom_cksum(START_EEPROM_DATA,CHKSUM_LEN,eeprom_buff) < 0) {
printk("\ncs89x0: EEPROM checksum bad, using default setup temporarily.\n");
cs_set_cnf( dev, lp );
}else {
/* get transmission control word but keep the autonegotiation bits */
if (!lp->auto_neg_cnf)
lp->auto_neg_cnf = eeprom_buff[AUTO_NEG_CNF_OFFSET/2];
/* Store adapter configuration */
if (!lp->adapter_cnf)
lp->adapter_cnf = eeprom_buff[ADAPTER_CNF_OFFSET/2];
/* Store ISA configuration */
lp->isa_config = eeprom_buff[ISA_CNF_OFFSET/2];
/* store the initial memory base address */
dev->mem_start = eeprom_buff[PACKET_PAGE_OFFSET/2] << 8;
#ifdef __bluecat__
if (macaddr_changed)
memcpy (dev->dev_addr, macaddr, sizeof (macaddr));
else
#endif
for (i = 0; i < ETH_ALEN/2; i++) {
dev->dev_addr[i*2] = eeprom_buff[i];
dev->dev_addr[i*2+1] = eeprom_buff[i] >> 8;
}
}
printk(" media %s%s%s",
(lp->adapter_cnf & A_CNF_10B_T)?"RJ-45,":"",
(lp->adapter_cnf & A_CNF_AUI)?"AUI,":"",
(lp->adapter_cnf & A_CNF_10B_2)?"BNC,":"");
lp->irq_map = 0xffff;
dev->irq = IRQ_EINT3;
printk(" IRQ %d, mac = ", dev->irq);
writereg(dev, PP_BusCTL, ENABLE_IRQ | MEMORY_ON);
writereg(dev, PP_CS8900_ISAINT, 0);
if (request_irq(dev->irq, &net_interrupt, 0, "cs89x0", dev)) {
printk("%s: IRQ conflict?\n", dev->name);
return -EAGAIN;
}
/* print the ethernet address. */
for (i = 0; i < ETH_ALEN; i++)
printk(" %2.2x", dev->dev_addr[i]);
/* Grab the region so we can find another board if autoIRQ fails. */
request_region(EPORT_ADDR, NETCARD_IO_EXTENT,"cs89x0");
dev->open = net_open;
dev->stop = net_close;
dev->hard_start_xmit = net_send_packet;
dev->get_stats = net_get_stats;
dev->set_multicast_list = &set_multicast_list;
dev->set_mac_address = &set_mac_address;
/* Fill in the fields of the device structure with ethernet values. */
ether_setup(dev);
#ifndef __bluecat__
printk(" probe1end...\n");
#else
printk("\n");
#endif
/* Enable interrupts*/
// writereg(dev, PP_BusCTL, ENABLE_IRQ | MEMORY_ON);
// writereg(dev, PP_CS8900_ISAINT, 0);
enable_irq(IRQ_EINT3);
return 0;
}
void
cs_set_cnf( struct device *dev, struct net_local *lp )
{
#ifndef __bluecat__
char macaddr[6] = { 0x00, 0xe0, 0x98, 0x06, 0x91, 0x66 };
#endif
int i;
if (!lp->auto_neg_cnf)
lp->auto_neg_cnf = 1;
/* Store adapter configuration */
lp->adapter_cnf = A_CNF_MEDIA_10B_T|A_CNF_10B_T|A_CNF_DC_DC_POLARITY;
lp->isa_config = 0;
dev->mem_start = EIOADDR;
for (i = 0; i < ETH_ALEN; i++)
dev->dev_addr[i] = macaddr[i];
}
__initfunc(void
reset_chip(struct device *dev))
{
int reset_start_time;
writereg(dev, PP_SelfCTL, readreg(dev, PP_SelfCTL) | POWER_ON_RESET);
/* wait 30 ms */
current->state = TASK_INTERRUPTIBLE;
schedule_timeout(30*HZ/1000);
/* Wait until the chip is reset */
reset_start_time = jiffies;
while( (readreg(dev, PP_SelfST) & INIT_DONE) == 0 &&
jiffies - reset_start_time < 2 )
;
}
static void
control_dc_dc(struct device *dev, int on_not_off)
{
struct net_local *lp = (struct net_local *)dev->priv;
unsigned int selfcontrol;
int timenow = jiffies;
/* control the DC to DC convertor in the SelfControl register. */
selfcontrol = HCB1_ENBL; /* Enable the HCB1 bit as an output */
if (((lp->adapter_cnf & A_CNF_DC_DC_POLARITY) != 0) ^ on_not_off)
selfcontrol |= HCB1;
else
selfcontrol &= ~HCB1;
writereg(dev, PP_SelfCTL, selfcontrol);
/* Wait for the DC/DC converter to power up - 500ms */
while (jiffies - timenow < 100)
;
}
static int
detect_tp(struct device *dev)
{
struct net_local *lp = (struct net_local *)dev->priv;
int timenow = jiffies;
if (net_debug > 1)
printk("%s: Attempting TP\n", dev->name);
/* If connected to another full duplex capable, 10-Base-T card link pulses
* seem to be lost when the auto detect bit in the LineCTL is set.
* To overcome this, auto detect bit will be cleared whilst testing the
* 10-Base-T interface. This would not be necessary for the sparrow chip but
* is simpler to do it anyway.
*/
writereg(dev, PP_LineCTL, lp->linectl &~ AUI_ONLY);
control_dc_dc(dev, 0);
/* Delay for the hardware to work out if the TP cable is present - 150ms */
for (timenow = jiffies; jiffies - timenow < 15; )
;
if ((readreg(dev, PP_LineST) & LINK_OK) == 0)
return 0;
return A_CNF_MEDIA_10B_T;
}
/* 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
net_open(struct device *dev)
{
struct net_local *lp = (struct net_local *)dev->priv;
int result = 0;
int i;
DEBUGNET2("cyn: net_open flags=0x%x type=0x%x\n", dev->flags, dev->type);
/* set the Ethernet address */
for (i=0; i < ETH_ALEN/2; i++)
writereg(dev, PP_IA+i*2,
dev->dev_addr[i*2] | (dev->dev_addr[i*2+1] << 8));
/* while we're testing the interface, leave interrupts disabled */
writereg(dev, PP_BusCTL, MEMORY_ON);
/* Set LineCTL quintuplet based on adapter configuration read from EEPROM */
if ( (lp->adapter_cnf & A_CNF_EXTND_10B_2) &&
(lp->adapter_cnf & A_CNF_LOW_RX_SQUELCH) )
lp->linectl = LOW_RX_SQUELCH;
else
lp->linectl = 0;
result = detect_tp(dev);
if (!result)
printk("%s: 10Base-T (RJ-45) has no cable\n", dev->name);
/* check "ignore missing media" bit */
if (lp->auto_neg_cnf & IMM_BIT)
/* Yes! I don't care if I see a link pulse */
result = A_CNF_MEDIA_10B_T;
if ( result == A_CNF_MEDIA_10B_T )
printk("%s: using 10Base-T (RJ-45)\n", dev->name);
else {
printk("%s: no network cable attached to configured media\n",
dev->name);
writereg(dev, PP_LineCTL, readreg(dev, PP_LineCTL)
& ~(SERIAL_TX_ON | SERIAL_RX_ON));
return -EAGAIN;
}
/* Turn on both receive and transmit operations */
writereg(dev, PP_LineCTL, readreg(dev, PP_LineCTL) |
SERIAL_RX_ON | SERIAL_TX_ON);
/* Receive only error free packets addressed to this card */
lp->rx_mode = 0;
writereg(dev, PP_RxCTL, DEF_RX_ACCEPT);
lp->curr_rx_cfg = RX_OK_ENBL | RX_CRC_ERROR_ENBL;
if (lp->isa_config & STREAM_TRANSFER)
lp->curr_rx_cfg |= RX_STREAM_ENBL;
writereg(dev, PP_RxCFG, lp->curr_rx_cfg);
writereg(dev, PP_TxCFG, TX_LOST_CRS_ENBL | TX_SQE_ERROR_ENBL | TX_OK_ENBL |
TX_LATE_COL_ENBL | TX_JBR_ENBL | TX_ANY_COL_ENBL | TX_16_COL_ENBL);
writereg(dev, PP_BufCFG, READY_FOR_TX_ENBL | RX_MISS_COUNT_OVRFLOW_ENBL |
TX_COL_COUNT_OVRFLOW_ENBL | TX_UNDERRUN_ENBL);
/* now that we've got our act together, enable everything */
writereg(dev, PP_BusCTL, ENABLE_IRQ);
dev->tbusy = 0;
dev->interrupt = 0;
dev->start = 1;
MOD_INC_USE_COUNT;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -