📄 cs89x0.c
字号:
/* * 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. * * 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 * * Copyright (C) 2002, 2003 Motorola Semiconductors HK Ltd * *//* cs89x0.c: A Crystal Semiconductor (Now Cirrus Logic) CS89[02]0 * driver for linux. *//* Written 1996 by Russell Nelson, with reference to skeleton.c written 1993-1994 by Donald Becker. This software may be used and distributed according to the terms of the GNU Public License, incorporated herein by reference. The author may be reached at nelson@crynwr.com, Crynwr Software, 521 Pleasant Valley Rd., Potsdam, NY 13676 Changelog: Mike Cruse : mcruse@cti-ltd.com : Changes for Linux 2.0 compatibility. : Added dev_id parameter in net_interrupt(), : request_irq() and free_irq(). Just NULL for now. Mike Cruse : Added MOD_INC_USE_COUNT and MOD_DEC_USE_COUNT macros : in net_open() and net_close() so kerneld would know : that the module is in use and wouldn't eject the : driver prematurely. Mike Cruse : Rewrote init_module() and cleanup_module using 8390.c : as 0040an example. Disabled autoprobing in init_module(), : not a good thing to do to other devices while Linux : is running from all accounts. Russ Nelson : Jul 13 1998. Added RxOnly DMA support. Melody Lee : Aug 10 1999. Changes for Linux 2.2.5 compatibility. : email: ethernet@crystal.cirrus.com Alan Cox : Removed 1.2 support, added 2.1 extra counters. Andrew Morton : andrewm@uow.edu.au : Kernel 2.3.48 : Handle kmalloc() failures : Other resource allocation fixes : Add SMP locks : Integrate Russ Nelson's ALLOW_DMA functionality back in. : If ALLOW_DMA is true, make DMA runtime selectable : Folded in changes from Cirrus (Melody Lee : <klee@crystal.cirrus.com>) : Don't call netif_wake_queue() in net_send_packet() : Fixed an out-of-mem bug in dma_rx() : Updated Documentation/cs89x0.txt*/static char *version ="cs89x0.c: (kernel 2.3.99) Russell Nelson, Andrew Morton\n";/* ======================= end of configuration ======================= *//* Always include 'config.h' first in case the user wants to turn on or override something. */#ifdef MODULE#include <linux/module.h>#include <linux/version.h>#else#define MOD_INC_USE_COUNT#define MOD_DEC_USE_COUNT#endif/* * Set this to zero to remove all the debug statements via * dead code elimination *//* Sources: Crynwr packet driver epktisa. Crystal Semiconductor data sheets.*/#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 <linux/spinlock.h>#include <linux/netdevice.h>#include <linux/etherdevice.h>#include <linux/skbuff.h>// Added by shin 2002.04#include <asm/irq.h>#include "type.h"#include "cs89x0.h"//Karen modify#define _reg_SIC_ID (*((volatile U32 *)(0xf021b804)))/* First, a few definitions that the brave might change. *//* A zero-terminated list of I/O addresses to be probed. */static unsigned int netcard_portlist[] __initdata ={ 0xf5000300, 0};#define DEBUGGING 1static unsigned int net_debug = 4;/* The number of low I/O ports used by the ethercard. */#define NETCARD_IO_EXTENT 16/* 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 */ 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 chip of this type, and return '0' if one exists. * Return ENODEV on Failure. */void set_interrupt(void) { _reg_PORTD_GIUS |= 0x00000080; // select GPIO - 17th pin _reg_PORTD_DDIR &= 0xFFFFFF7F; // config as input _reg_PORTD_IMR |= 0x00000080; // enable interrupt _reg_PORTD_ICR1 &= 0xFFFFBFFF; // config as positive edge trigger _reg_PORTD_ICR1 |= 0x8000;_reg_PORTD_PUEN |= 0xFFFFFF7F; // pull up enable interrupt _reg_INTENNUM=62; _reg_INTENABLEH |=1<<30; // (*((volatile U32 *)(0xf021c30c))) &=0xffffdfff; //(*((volatile U32 *)(0xf021c30c))) |=0x1000; } void Clear_interrupt(void) { _reg_PORTD_ISR = 0x00000080; } int __init cs89x0_probe(struct net_device *dev){ static int initialised = 0; int i; if (initialised) return -ENODEV; initialised = 1; if (net_debug) printk("cs89x0:cs89x0_probe()\n"); for (i = 0; netcard_portlist[i]; i++) { int ioaddr = netcard_portlist[i]; if (cs89x0_probe1(dev, ioaddr) == 0) return 0; } printk(KERN_WARNING "cs89x0: no cs8900 detected.\n"); return ENODEV;}u8 inlineraw_readb(u32 addr){ return (*(volatile u8 *)(addr));}u16 inlineraw_readw(u32 addr){ return (*(volatile u16 *)(addr));}void inlineraw_writeb(u8 value, u32 addr){ (*(volatile u8 *)(addr)) = value;}void inlineraw_writew(u16 value, u32 addr){ (*(volatile u16 *)(addr)) = value;}u16 inlinereadreg(struct net_device *dev, int portno){ raw_writew(portno, dev->base_addr + ADD_PORT); return raw_readw(dev->base_addr + DATA_PORT);}void inlinewritereg(struct net_device *dev, int portno, u16 value){ raw_writew(portno, dev->base_addr + ADD_PORT); raw_writew(value, dev->base_addr + DATA_PORT);}u16 inlinereadword(struct net_device *dev, int portno){ return raw_readw(dev->base_addr + portno);}void inlinewriteword(struct net_device *dev, int portno, u16 value){ raw_writew(value, dev->base_addr + portno);}static 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); }}static void readblock(struct net_device *dev, char *pData, int Length){ u16 wOddWord; int i; u16 StartOffset = PP_RxFrame | AUTOINCREMENT; writeword(dev, ADD_PORT, StartOffset); if ((u32) pData % 2) { for (i=0; i < (Length/2); i++) { wOddWord = readword(dev, DATA_PORT); *(u8*)pData++ = (u8) wOddWord & 0xFF; *(u8*)pData++ = (u8) (wOddWord >> 8) & 0xFF; } } else { for (i=0; i < (Length/2); i++) { *(u16*) pData = readword(dev, DATA_PORT); pData += 2; } } switch (Length % 2) { case 1: *pData = (u8) (readword(dev, DATA_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. */u16 MacAddr[3] = {0,0,0};static int __initcs89x0_probe1(struct net_device *dev, int ioaddr){ struct net_local *lp; static unsigned version_printed = 0; int i; unsigned rev_type = 0; int retval; unsigned short wData; /* Initialize the device structure. */ if (dev->priv == NULL) { dev->priv = kmalloc(sizeof(struct net_local), GFP_KERNEL); if (dev->priv == 0) { retval = ENOMEM; goto out; } memset(dev->priv, 0, sizeof(struct net_local)); } lp = (struct net_local *)dev->priv; wData = raw_readw(ioaddr + ADD_PORT); if ((wData & 0xF000) != 0x3000 ) { if (net_debug) printk("cs89x0:cs89x0_probe1() 0x%08X\n", wData); retval = ENODEV; goto out1; } /* Fill in the 'dev' fields. */ dev->base_addr = ioaddr; //printk("ioaddr=%x\n",ioaddr); /* 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 the 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 (lp->chip_type != CS8900 && lp->chip_revision >= 'C') lp->send_cmd = TX_NOW; //reset_chip(dev); if (net_debug && version_printed++ == 0) printk(version); printk(KERN_INFO "%s: cs89%c0%s rev %c Base 0x%08X", dev->name, lp->chip_type==CS8900 ? '0' : '2', lp->chip_type==CS8920M ? "M" : "", lp->chip_revision, (unsigned int)dev->base_addr); /* * Read MAC address from EEPROM */ // Use MAC address of bootldr for (i=0; i < ETH_ALEN/2; i++) { dev->dev_addr[i*2] = MacAddr[i]; dev->dev_addr[i*2+1] = MacAddr[i] >> 8; } dev->irq = 62; printk(KERN_INFO ", IRQ %d", dev->irq); /* print the ethernet address. */ printk(", MAC "); for (i = 0; i < ETH_ALEN; i++) { printk("%s%02X", i ? ":" : "", dev->dev_addr[i]); } printk("\n"); dev->open = net_open; dev->stop = net_close; dev->tx_timeout = net_timeout; dev->watchdog_timeo = HZ; 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); return 0; out1: kfree(dev->priv); dev->priv = 0; out: return retval;}void __init reset_chip(struct net_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 < 4000);}/* 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. *//* AKPM: do we need to do any locking here? */static intnet_open(struct net_device *dev){ struct net_local *lp = (struct net_local *)dev->priv; int i; /* CS8900A INTRQ0 is connected to MOSI. */ set_interrupt(); /* Prevent the crystal chip from generating interrupts */ writereg(dev, PP_BusCTL, readreg(dev, PP_BusCTL) & ~ENABLE_IRQ); /* Grab the interrupt */ if (request_irq(dev->irq, &net_interrupt, SA_SHIRQ, "cs89x0", dev)) { if (net_debug) printk("cs89x0: request_irq(%d) failed\n", dev->irq); return -EAGAIN; } /* Set up the IRQ - Apparently magic */ if (lp->chip_type == CS8900) { writereg(dev, PP_CS8900_ISAINT, 0); } else { writereg(dev, PP_CS8920_ISAINT, 0); } /* set the Ethernet address */ for (i=0; i < ETH_ALEN/2; i++)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -