📄 cs8900-2624.c
字号:
/*********************************** * Author: Nolan * Date: 2008-08-19 * Description: Driver for cs8900 ethernet chip, * support linux kernel 2.6.24 * Version: 0.2 ***********************************/ #include <linux/module.h>#include <linux/sched.h>#include <linux/ioport.h>#include <linux/delay.h>#include <linux/irq.h>#include <linux/netdevice.h>#include <linux/etherdevice.h>#include <linux/skbuff.h>#include <linux/platform_device.h>#include <asm/io.h>#include <asm/uaccess.h>#include <asm/hardware.h>#include <asm/arch/regs-mem.h>#include "cs8900-2624.h"#define DRV_NAME "CS8900-2624"#define VER_STR "Crystal Semiconductor ethernet chip CS8900A driver, for linux-2.6.24, adapted for s3c2410"struct cs8900_dev { unsigned long phy_addr; /* 8900 I/O space address */ unsigned long addr_len; /* 8900 I/O space length */ void __iomem *ioaddr; /* 8900 virtual address */ struct net_device *netdev; struct net_device_stats cs8900_stats; u16 txlen; /* length of last tx frame */ spinlock_t lock; /* lock */};/* function prototype */static void cs8900_reset(struct net_device *);static void cs8900_cleanup(struct net_device *);static void cs8900_hardware_init(struct net_device *);static irqreturn_t cs8900_interrupt(int, void *);static void cs8900_irq_receive(struct net_device *, u16);static void cs8900_irq_transmit(struct net_device *, u16);static void cs8900_irq_buffer(struct net_device *, u16);/********************************* * read data from cs8900 PacketPage register * reg: offset of register * return value: content of register ********************************/static inline u16 cs8900_read(struct net_device *dev, u16 reg){ u16 ret; iowrite16(reg, (void __iomem *)(dev->base_addr + CS8900_PPtr)); ret = ioread16((void __iomem *)(dev->base_addr + CS8900_PData)); return ret;}/********************************* * write data from cs8900 PacketPage register * reg: offset of register * value: content of register ********************************/static inline voidcs8900_write(struct net_device *dev, u16 reg, u16 value){ iowrite16(reg, (void __iomem *)(dev->base_addr + CS8900_PPtr)); iowrite16(value, (void __iomem *)(dev->base_addr + CS8900_PData));}/********************************** * set cs8900 register * reg: offset * value: bits to set *********************************/static inline void cs8900_set(struct net_device *dev, u16 reg, u16 value){ u16 ret; ret = cs8900_read(dev, reg); cs8900_write(dev, reg, (ret|value));}/********************************* * clear cs8900 register * reg: offset * value: bits to clear ********************************/static inline voidcs8900_clear(struct net_device *dev, u16 reg, u16 value){ u16 ret; ret = cs8900_read(dev, reg); cs8900_write(dev, reg, (ret&~value));}/******************************** * read one frame from cs8900 Receive buffer * length: frame length in bytes *******************************/static inline voidcs8900_frame_read(struct net_device *dev, struct sk_buff *skb, u16 length){ unsigned long count = (length + 1)/2; void __iomem *ptr = (void *)(dev->base_addr + CS8900_Data); /* read data from cs8900 Receive buffer */ ioread16_rep(ptr, skb_put(skb, length), count);}/******************************** * write one frame to cs8900 Transmit buffer *******************************/static inline void cs8900_frame_write(struct net_device *dev, struct sk_buff *skb){ unsigned long count = (skb->len + 1)/2; void __iomem *ptr = (void *)(dev->base_addr + CS8900_Data); /* Write frame to cs8900 transmit buffer */ iowrite16_rep(ptr, skb->data, count);}/*************************** * cs8900_reset: reset chip **************************/static void cs8900_reset(struct net_device *ndev){ int i; /* Ascertained that cs8900 chip has finished reset. */ cs8900_set(ndev, PP_SelfCTL, RESET); for (i=1000; i>0; i--) { barrier(); if ((cs8900_read(ndev, PP_SelfCTL) & RESET) == 0) { printk("Cs8900 has finished reset.\n"); break; } udelay(10); }}/******************************* * cs8900_cleanup: release any mapped resources ******************************/static void cs8900_cleanup(struct net_device *ndev){ struct cs8900_dev *priv = netdev_priv(ndev); if (NULL == ndev) return; if (NULL != priv->ioaddr) iounmap(priv->ioaddr); if (0 != priv->phy_addr) release_mem_region(priv->phy_addr, priv->addr_len);}/*********************************** * cs8900_hardware_init * Initialize cs8900 register **********************************/static void cs8900_hardware_init(struct net_device *ndev){ u16 value; /* RxCFG */ value = RxOKiE | /* Enable RxOK int */ BufferCRC | /* Received frame include CRC */ CRCerroriE | /* Enable CRC error int */ RuntiE | /* Enable int when receive <64bytes frame */ ExtradataiE; /* Enable int when receive >1518bytes frame */ cs8900_set(ndev, PP_RxCFG, value); /* RxCTL */ value = RxOKA | /* Receive normal frame */ IndividualA | /* Receive individual frame */ BroadcastA; /* Receive broadcast frame */ cs8900_set(ndev, PP_RxCTL, value); /* TxCFG */ value = TxOKiE | /* Enable TxOK int */ Out_of_windowiE | /* Enable out-of-window int */ JabberiE | /* Enable Jabber int */ T16colliE; /* Enable 16 collisions int */ cs8900_set(ndev, PP_TxCFG, value); /* BufCFG */ value = Rdy4TxiE | /* Enable transmit ready int */ RxMissiE | /* Enable Rx miss frame int */ TxColOvfiE | /* Enable TxCOL counter overflow int */ MissOvfloiE; /* Enable Rx miss counter over int */ cs8900_set(ndev, PP_BufCFG, value); /* LineCTL */ value = SerRxON | /* Enable receiver */ SerTxON; /* Enable transmitter */ cs8900_set(ndev, PP_LineCTL, value); /* SelfCTL: no need */ /* BusCTL */ value = EnableRQ; /* Enable IRQ */ cs8900_set(ndev, PP_BusCTL, value);#ifdef CONFIG_FULL_DUPLEX /* TestCTL */ value = FDX; /* Enable full duplex */ cs8900_set(ndev, PP_TestCTL, value);#endif /* FULL_FUPLEX */}/*********************************** * cs8900_open * open device. called when ndev->flags has been changed **********************************/static intcs8900_open(struct net_device *ndev){ int result; printk(KERN_ERR "cs8900 entered cs8900_open\n"); /* set irq type */ //set_irq_type(ndev->irq, IRQT_RISING); set_irq_type(ndev->irq, IRQT_BOTHEDGE); /* Initialize hardware */ cs8900_hardware_init(ndev); udelay(200); /* wait a moment */ /* request irq */ result = request_irq(ndev->irq, cs8900_interrupt, IRQF_SHARED, ndev->name, ndev); if (result) { printk("%s: could not register irq %d routine \n", ndev->name, ndev->irq); return result; } /* start tx queue */ netif_start_queue(ndev); return 0;}/****************************** * cs8900_stop * called when ifdown *****************************/static intcs8900_stop(struct net_device *ndev){ /* stop tx queue */ netif_stop_queue(ndev); /* unregister interrupt routine */ free_irq(ndev->irq, ndev); /* clear all register */ cs8900_write(ndev, PP_BusCTL, 0); cs8900_write(ndev, PP_TestCTL, 0); cs8900_write(ndev, PP_SelfCTL, 0); cs8900_write(ndev, PP_LineCTL, 0); cs8900_write(ndev, PP_BufCFG, 0); cs8900_write(ndev, PP_TxCFG, 0); cs8900_write(ndev, PP_RxCTL, 0); cs8900_write(ndev, PP_RxCFG, 0); return 0;}/*************************************** * cs8900_get_stats * return rx&tx statistics value **************************************/static struct net_device_stats *cs8900_get_stats(struct net_device *ndev){ return &ndev->stats;// struct cs8900_dev *priv = netdev_priv(ndev);// return &priv->cs8900_stats;}/************************************** * cs8900_transmit_timeout * called after transmit has stopped ndev->watchdog_timeo **************************************/static void cs8900_transmit_timeout(struct net_device *ndev){ struct cs8900_dev *priv = netdev_priv(ndev); /* reset cs8900 */// cs8900_reset(ndev); /* reinitialize hardware */// cs8900_hardware_init(ndev);// udelay(200); /* update net_device_stats */ ndev->stats.tx_errors++; ndev->stats.tx_heartbeat_errors++; /* update cs8900_priv */ spin_lock_irq(&priv->lock); /* protect txlen */ priv->txlen = 0; spin_unlock_irq(&priv->lock); /* wake up tx queue */ netif_wake_queue(ndev);}/****************************************** * cs8900_send_start: * Send one frame to cs8900 chip *****************************************/static intcs8900_send_start(struct sk_buff *skb, struct net_device *ndev){ struct cs8900_dev *priv = netdev_priv(ndev); u16 status; int i; printk("CS8900 enter send_start()\n"); /* suspend tx queue, until next tx interrupt occur */ netif_stop_queue(ndev); /* Send TxCMD to cs8900: after 5 bytes */ cs8900_write(ndev, PP_TxCMD, TxStart(After5)); /* Send TxLength: from skb */ cs8900_write(ndev, PP_TxLen, skb->len); /* Get transmit status, read TxBidErr */ status = cs8900_read(ndev, PP_BusST); if (status & TxBidErr) { printk(KERN_WARNING "%s: Invalid frame size %d!\n", ndev->name, skb->len); ndev->stats.tx_errors++; ndev->stats.tx_aborted_errors++; return 1; } if (status & Rdy4TxNOW) printk("In %s, Ready to transmit\n", __FUNCTION__); /* Wait for Transmitter ready */ for (i=0; i<10; i++) { if(status & Rdy4TxNOW) goto ready_to_tx; udelay(10); } printk(KERN_WARNING "%s: Transmit buffer not free!\n", ndev->name); ndev->stats.tx_errors++; return 1;ready_to_tx: /* send one frame */ cs8900_frame_write(ndev, skb); /* Record tx frame length to priv->txlen */ spin_lock_irq(&priv->lock); priv->txlen = skb->len; spin_unlock_irq(&priv->lock); /* Record last tx time */ ndev->trans_start = jiffies; /* free skb */ dev_kfree_skb(skb);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -