📄 s3c4510.c
字号:
/*
* linux/deriver/net/s3c4510.c
* Ethernet driver for Samsung 4510B
* Copyright (C) 2002 Mac Wang <mac@os.nctu.edu.tw>
*
* Revised by Zheng Geng email: gzheng@sohu.com 2004.4
* fix the s3c4510_rx to check if the BDMA is hang
* Add set_mac_address to change mac address when up
* fix other spinlock problems in other fuction
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/kernel.h> // printk()
#include <linux/slab.h> // kmalloc()
#include <linux/errno.h> // error codes
#include <linux/types.h> // size_t
#include <linux/interrupt.h> // mark_bh
#include <linux/in.h>
#include <linux/netdevice.h> // net_device
#include <linux/etherdevice.h>
#include <linux/ip.h>
#include <linux/tcp.h>
#include <linux/skbuff.h>
#include <asm/irq.h>
#include "s3c4510.h"
#undef DEBUG
#ifdef DEBUG
#define TRACE(str, args...) printk("S3C4510 eth: " str, ## args)
#else
#define TRACE(str, args...)
#endif
static int timeout = 100; // tx watchdog ticks 100 = 1s
static char *version = "Samsung S3C4510 Ethernet driver version 0.1 (2002-02-20) <mac@os.nctu.edu.tw>\n";
/*
* This structure is private to each device. It is used to pass
* packets in and out, so there is place for a packet
*/
struct s3c4510_priv {
struct net_device_stats stats;
unsigned long tx_ptr;
unsigned long gtx_ptr;
unsigned long rx_ptr;
// Frame Descriptor linked list for S3C4510 MAC, initialized by FD_init()
volatile struct FrameDesc rx_fd[RX_FRAME_SIZE];
volatile struct FrameDesc tx_fd[TX_FRAME_SIZE];
// Frame Buffer for S3C4510 MAC
volatile struct ethframe rx_buf[RX_FRAME_SIZE];
volatile struct ethframe tx_buf[TX_FRAME_SIZE];
spinlock_t lock;
struct sk_buff *skb;
};
/* --------------------- *
* MII support functions *
* --------------------- */
void MIIWrite(unsigned long PhyInAddr, unsigned long PhyAddr, unsigned long PhyData)
{
CSR_WRITE(STADATA, PhyData);
CSR_WRITE(STACON, PhyInAddr | PhyAddr | MiiBusy | PHYREGWRITE);
while(CSR_READ(STACON) & MiiBusy);
}
unsigned long MIIRead(unsigned long PhyInAddr, unsigned long PhyAddr)
{
CSR_WRITE(STACON, PhyInAddr | PhyAddr | MiiBusy);
while(CSR_READ(STACON) & MiiBusy);
return CSR_READ(STADATA);
}
/*
*
*/
void FD_Init(struct net_device *dev)
{
struct s3c4510_priv *priv = (struct s3c4510_priv *) dev->priv;
struct FrameDesc *FD_ptr;
struct FrameDesc *FD_start_ptr;
struct FrameDesc *FD_last_ptr = NULL;
unsigned long FB_base;
unsigned long i;
// TxFDInitialize() in diag code
// Get Frame descriptor's base address
// +0x4000000 is for setting this area to non-cacheable area.
CSR_WRITE(BDMATXPTR, (unsigned long) priv->tx_fd + 0x4000000);
priv->gtx_ptr = priv->tx_ptr = CSR_READ(BDMATXPTR);
// Get Transmit buffer base address.
FB_base = (unsigned long) priv->tx_buf + 0x4000000;
// Generate linked list.
FD_start_ptr = FD_ptr = (struct FrameDesc *)priv->tx_ptr;
FD_last_ptr = NULL;
for(i = 0; i < TX_FRAME_SIZE; i++) {
if(FD_last_ptr == NULL)
FD_last_ptr = FD_ptr;
else
FD_last_ptr -> NextFrameDescriptor = (unsigned long)FD_ptr;
FD_ptr->FrameDataPtr = (unsigned long)(FB_base & CPU_owner);
FD_ptr->Reserved = 0;
FD_ptr->StatusAndFrameLength = (unsigned long)0x0;
FD_last_ptr = FD_ptr;
FD_ptr++;
FB_base += sizeof(struct ethframe);
}
//Make Frame descriptor to ring buffer type.
FD_last_ptr->NextFrameDescriptor = (unsigned long)FD_start_ptr;
// RxFDInitialize() in diag code
// Get Frame descriptor's base address
CSR_WRITE(BDMARXPTR, (unsigned long) priv->rx_fd + 0x4000000);
priv->rx_ptr = CSR_READ(BDMARXPTR);
// Get Transmit buffer base address
FB_base = (unsigned long)priv->rx_buf + 0x4000000;
// Generate linked list
FD_start_ptr = FD_ptr = (struct FrameDesc *)priv->rx_ptr;
FD_last_ptr = NULL;
for(i = 0; i < RX_FRAME_SIZE; i++) {
if(FD_last_ptr == NULL )
FD_last_ptr = FD_ptr;
else
FD_last_ptr -> NextFrameDescriptor = (unsigned long)FD_ptr;
FD_ptr->FrameDataPtr = (unsigned long)(FB_base | BDMA_owner);
FD_ptr->Reserved = 0;
FD_ptr->StatusAndFrameLength = (unsigned long)0x0;
FD_ptr->NextFrameDescriptor = 0x0;
FD_last_ptr = FD_ptr;
FD_ptr++;
FB_base += sizeof(struct ethframe);
}
// Make Frame descriptor to ring buffer type.
FD_last_ptr->NextFrameDescriptor = (unsigned long)FD_start_ptr;
}
/*
* rx
*/
void s3c4510_rx(int irq, void *dev_id, struct pt_regs *regs)
{
// int i;
int len;
unsigned char *data;
struct sk_buff *skb;
struct FrameDesc *FD_ptr;
unsigned long CFD_ptr;
unsigned long RxStat;
unsigned long BDMAStat;
struct net_device *dev = (struct net_device *) dev_id;
struct s3c4510_priv *priv = (struct s3c4510_priv *) dev->priv;
TRACE("rx\n");
spin_lock(&priv->lock);
// 1. Get current frame descriptor and status
CFD_ptr = CSR_READ(BDMARXPTR);
BDMAStat = CSR_READ(BDMASTAT);
// 2. Clear BDMA status register bit by write 1
CSR_WRITE(BDMASTAT, BDMAStat | S_BRxRDF);
do {
// 3. Check Null List Interrupt
/*
if(CSR_READ(BDMASTAT) & BRxNL) {
CSR_WRITE(BDMASTAT, );
}
*/
// 4. Get Rx Frame Descriptor
FD_ptr = (struct FrameDesc *) priv->rx_ptr;
RxStat = (FD_ptr->StatusAndFrameLength >> 16) & 0xffff;
// 5. If Rx frame is good, then process received frame
if(RxStat & Good){
len = (FD_ptr->StatusAndFrameLength & 0xffff) - 4;
data = (unsigned char *) FD_ptr->FrameDataPtr + 2;
// 6. Get received frame to memory buffer
skb = dev_alloc_skb(len+2);
if(!skb) {
printk("S3C4510 eth: low on mem - packet dropped\n");
priv->stats.rx_dropped++;
spin_unlock(&priv->lock);
return;
}
// memcpy(skb_put(skb, len), data, len);
/*
printk("len: %d\n", len);
for(i = 0; i < len; i++)
{
printk("%3x", data[i]);
if((i+1)%16==0)
printk("\n");
}
printk("\n");
*/
skb->dev = dev;
skb_reserve(skb, 2);
skb_put(skb, len);
eth_copy_and_sum(skb, data, len, 0);
skb->protocol = eth_type_trans(skb, dev);
priv->stats.rx_packets++;
priv->stats.rx_bytes += len;
netif_rx(skb);
}
else {
// 7. If Rx frame has error, then process err frame
priv->stats.rx_errors++;
if(RxStat & LongErr)
priv->stats.rx_length_errors++;
if(RxStat & OvMax)
priv->stats.rx_over_errors++;
if(RxStat & CRCErr)
priv->stats.rx_crc_errors++;
if(RxStat & AlignErr)
priv->stats.rx_frame_errors++;
if(RxStat & Overflow)
priv->stats.rx_fifo_errors++;
}
// 8. Change ownership to BDMA for next use
FD_ptr -> FrameDataPtr |= BDMA_owner;
// Save Current Status and Frame Length field, and clear
FD_ptr -> StatusAndFrameLength = 0x0;
// 9. Get Next Frame Descriptor pointer to process
priv->rx_ptr = (unsigned long)(FD_ptr -> NextFrameDescriptor);
}while(CFD_ptr != priv->rx_ptr);
// 10. Check Notowner status
if(CSR_READ(BDMASTAT) & S_BRxNO) {
CSR_WRITE(BDMASTAT, S_BRxNO);
}
//check if the BDMA is hang
if (! (CSR_READ(BDMARXCON) & BRxEn))
CSR_WRITE(BDMARXCON, gBDMARXCON);
spin_unlock(&priv->lock);
}
/*
* tx
*/
void s3c4510_tx(int irq, void *dev_id, struct pt_regs *regs)
{
struct FrameDesc *FD_ptr;
unsigned long CFD_ptr;
unsigned long *FB_ptr;
unsigned long status;
struct net_device *dev = (struct net_device *) dev_id;
struct s3c4510_priv *priv = (struct s3c4510_priv *) dev->priv;
TRACE("tx\n");
spin_lock(&priv->lock);
CFD_ptr = CSR_READ(BDMATXPTR);
while(priv->gtx_ptr != CFD_ptr)
{
FD_ptr = (struct FrameDesc *) priv->gtx_ptr;
FB_ptr = (unsigned long *) &FD_ptr->FrameDataPtr;
if(!(*FB_ptr & BDMA_owner))
{
status = (FD_ptr->StatusAndFrameLength >> 16) & 0xffff;
if(status & Comp) {
priv->stats.tx_packets++;
}
else {
priv->stats.tx_errors++;
if(status & TxPar)
priv->stats.tx_aborted_errors++;
if(status & NCarr)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -