⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 bluecat_cs8900a.c

📁 嵌入式linux系统CS8900A驱动程序
💻 C
📖 第 1 页 / 共 2 页
字号:
/* 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 + -