linux2.6.30.4基于platform_driver的cs8900(s3c2410)的驱动程序

最近在开发板上搞2.6.30内核移植,从网上下载了不少别人移植好的驱动,我下载下来却不能用。终于下定决心自己改写一个适合的驱动,搞了几天,终于搞定了。是基于platform_driver的,发表出来大家共享一下。头文件是用的网上的,只需要修改./drivers/net下的Kconfig文件和Makfile,把cs8900.c加进去即可,具体方法可参考网上的文章。别的不需要修改任何文件。以下是完整的cs8900.c:


/*
 * linux/drivers/net/cs8900.c
 *
 * Author: Abraham van der Merwe <abraham at 2d3d.co.za>
 *
 * A Cirrus Logic CS8900A driver for Linux
 * based on the cs89x0 driver written by Russell Nelson,
 * Donald Becker, and others.
 *
 * This source code is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * version 2 as published by the Free Software Foundation.
 *
 * History:
 *    22-May-2002  Initial version (Abraham vd Merwe)
 *    30-May-2002  Added char device support for eeprom (Frank Becker)
 *    24-Jan-2004  Fixups for 2.6 (Frank Becker)
 *      15-July-2004 Modified for SMDK2410 (Roc Wu pwu at jadechip.com)
 */
 
#define VERSION_STRING "Cirrus Logic CS8900A driver for Linux (Modified for SMDK2410)"

/*
 * At the moment the driver does not support memory mode operation.
 * It is trivial to implement this, but not worth the effort.
 */

/*
 * TODO:
 *
 *   1. Sort out ethernet checksum
 *   2. If !ready in send_start(), queue buffer and send it in interrupt handler
 *      when we receive a BufEvent with Rdy4Tx, send it again. dangerous!
 *   3. how do we prevent interrupt handler destroying integrity of get_stats()?
 *   4. Change reset code to check status.
 *   5. Implement set_mac_address and remove fake mac address
 *   7. Link status detection stuff
 *   8. Write utility to write EEPROM, do self testing, etc.
 *   9. Implement DMA routines (I need a board w/ DMA support for that)
 *  10. Power management
 *  11. Add support for multiple ethernet chips
 */

// added BSt
//#include <linux/config.h>

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/version.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/ioport.h>
#include <asm/irq.h>
//#include <asm/hardware.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include <linux/platform_device.h>
#include <linux/irq.h>
#include <linux/interrupt.h>

#include <asm/mach-types.h>

#include "cs8900.h"
#define CARDNAME    "cs8900"
#define DRV_VERSION    "1.01"
#define res_size(_r) (((_r)->end - (_r)->start) + 1)
u8 MAC_Addr[6] = {0x08,0x00,0x3e,0x26,0x0a,0x5b};
u16 txlen;
struct cs8900_t {
    struct net_device_stats stats;
//    u16 txlen;
    int char_devnum;
    void __iomem    *io_addr;    /* Register I/O base address */
    void __iomem    *irq_addr;    /* Register I/O base address */

    unsigned int irq;
    unsigned int    flags;
    unsigned int    in_suspend :1;
    struct net_device *ndev;
    struct device    *dev;         /* parent device */
    struct resource    *io_res;   /* resources found */
    struct resource    *irq_res;   /* resources found */
    struct resource    *irq_area;   /* resources found */

    struct resource    *addr_req;
    struct mutex     addr_lock;    /* phy and eeprom access lock */
    spinlock_t    lock;
} ;

#define    IO_DATA        0x00
#define    IO_CMD        0x04
#define    IO_LEN        0x06
#define    IO_ISQ        0x08
#define    IO_PPTR        0x0a
#define    IO_PDATA    0x0c
static u16 read_ISQ(struct net_device *dev)
{
    return readw(dev->base_addr + IO_ISQ);
}
static void write_TXCMD(struct net_device *dev,u16 cmd)
{
    writew(cmd,dev->base_addr + IO_CMD);
}
static void write_TXLEN(struct net_device *dev,u16 len)
{
    writew(len,dev->base_addr + IO_LEN);
}
static void write_cs8900(struct net_device *dev,u16 reg,u16 value)
{
    writew(reg,dev->base_addr + IO_PPTR);
    writew(value,dev->base_addr + IO_PDATA);
}
static u16 read_cs8900(struct net_device *dev,u16 reg)
{
    writew(reg,dev->base_addr + IO_PPTR);
    return readw(dev->base_addr + IO_PDATA);
}
static inline void read_Frame (struct net_device *dev,struct sk_buff *skb,u16 length)
{
    insw (dev->base_addr,skb_put (skb,length),(length + 1) / 2);
}
static inline void write_Frame (struct net_device *dev,struct sk_buff *skb)
{
    outsw (dev->base_addr,skb->data,(skb->len + 1) / 2);
}

static void cs8900_receive (struct net_device *dev)
{
    struct sk_buff *skb;
    u16 status,length;

    status = read_cs8900 (dev,PP_RxStatus);
    length = read_cs8900 (dev,PP_RxLength);

    if (!(status & RxOK)) {
        dev->stats.rx_errors++;
        if ((status & (Runt | Extradata))) dev->stats.rx_length_errors++;
        if ((status & CRCerror)) dev->stats.rx_crc_errors++;
        return;
    }

    if ((skb = dev_alloc_skb (length + 4)) == NULL) {
        dev->stats.rx_dropped++;
        return;
    }

    skb->dev = dev;
    skb_reserve (skb,2);

    read_Frame (dev,skb,length);

#ifdef FULL_DUPLEX
    dump_packet (dev,skb,"recv");
#endif    /* #ifdef FULL_DUPLEX */

    skb->protocol = eth_type_trans (skb,dev);

    netif_rx (skb);
    dev->last_rx = jiffies;

    dev->stats.rx_packets++;
    dev->stats.rx_bytes += length;
}

static irqreturn_t s3c24xx_cs8900_irq(int irqno, void *id)//struct pt_regs *regs
{
    struct net_device *dev = id;
    struct cs8900_t *priv = netdev_priv(dev);
    volatile u16 status;
    unsigned long flags;

    spin_lock_irqsave(&priv->lock,flags);
    while (status = read_ISQ(dev))
    {
        switch (RegNum (status)) {
        case RxEvent:
            cs8900_receive (dev);
            break;

        case TxEvent:
            dev->stats.collisions += ColCount (read_cs8900 (dev,PP_TxCOL));
            if (!(RegContent (status) & TxOK)) {
                dev->stats.tx_errors++;
                if ((RegContent (status) & Out_of_window)) dev->stats.tx_window_errors++;
                if ((RegContent (status) & Jabber)) dev->stats.tx_aborted_errors++;
                break;
            } else if (txlen) {
                dev->stats.tx_packets++;
                dev->stats.tx_bytes += txlen;
            }
            txlen = 0;
            netif_wake_queue (dev);
            break;

        case BufEvent:
            if ((RegContent (status) & RxMiss)) {
                u16 missed = MissCount (read_cs8900 (dev,PP_RxMISS));
                dev->stats.rx_errors += missed;
                dev->stats.rx_missed_errors += missed;
            }
            if ((RegContent (status) & TxUnderrun)) {
                dev->stats.tx_errors++;
                dev->stats.tx_fifo_errors++;

                txlen = 0;
                netif_wake_queue (dev);
            }
            /* if Rdy4Tx, transmit last sent packet (if any) */
            break;

        case TxCOL:
            dev->stats.collisions += ColCount (read_cs8900 (dev,PP_TxCOL));
            break;

        case RxMISS:
            status = MissCount (read_cs8900 (dev,PP_RxMISS));
            dev->stats.rx_errors += status;
            dev->stats.rx_missed_errors += status;
            break;
        }
    }
    spin_unlock_irqrestore(&priv->lock, flags);
    return IRQ_HANDLED;
}
#define __IRQT_RISEDGE  (1 << 1)
#define __IRQT_HIGHLVL  (1 << 3)
#define IRQT_RISING     (__IRQT_RISEDGE)
#define IRQT_HIGH       (__IRQT_HIGHLVL)
static int
cs8900_open(struct net_device *dev)
{
    int ret;
    struct cs8900_t *priv = netdev_priv(dev);

    ret = request_irq(dev->irq, s3c24xx_cs8900_irq, priv->flags,dev->name, dev);
    if (ret != 0) {
        printk ("%s: could not register interrupt %d/n",dev->name, dev->irq);
        return -ENOENT;
    }
    write_cs8900 (dev,PP_RxCFG,    0x03|RxOKiE | BufferCRC | CRCerroriE | RuntiE | ExtradataiE);
    write_cs8900 (dev,PP_RxCTL,    0x05|RxOKA | IndividualA | BroadcastA);
    write_cs8900 (dev,PP_TxCFG,    0x07|TxOKiE | Out_of_windowiE | JabberiE);
    write_cs8900 (dev,PP_BufCFG,    0x0b|Rdy4TxiE | RxMissiE | TxUnderruniE | TxColOvfiE | MissOvfloiE);
    write_cs8900 (dev,PP_LineCTL,    0x13|SerRxON | SerTxON);
    write_cs8900 (dev,PP_BusCTL,    0x17|EnableRQ);
    writew(0x10,priv->irq_addr+0x8c);
//    writew(readw(priv->irq_addr+0x60)|0x8,priv->irq_addr+0x60);
    /* start the queue */
    netif_start_queue (dev);

    return (0);
}

static void cs8900_timeout(struct net_device *dev)
{
//    struct cs8900_t *priv = netdev_priv(dev);
    dev->stats.tx_errors++;
    dev->stats.tx_heartbeat_errors++;
    txlen = 0;
    netif_wake_queue (dev);
}

static struct net_device_stats *cs8900_get_stats (struct net_device *dev)
{
//    struct cs8900_t *priv = netdev_priv(dev);
    return (&dev->stats);
}

static int
cs8900_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
    struct cs8900_t *priv = netdev_priv(dev);
    unsigned long flags;
    u16 status;
    int i;

    spin_lock_irqsave(&priv->lock,flags);
    netif_stop_queue (dev);

    write_TXCMD (dev,TxStart (After5));
    write_TXLEN (dev,skb->len);
    txlen = skb->len;
    status = read_cs8900 (dev,PP_BusST);
//    printk (KERN_ERR "line status: 0x%x/n",read_cs8900 (dev,0x134));

    if ((status & TxBidErr)) {
        spin_unlock_irqrestore(&priv->lock,flags);
        printk (KERN_WARNING "%s: Invalid frame size %d!/n",dev->name,skb->len);
        dev->stats.tx_errors++;
        dev->stats.tx_aborted_errors++;
        txlen = 0;
        return (1);
    }

    if (!(status & Rdy4TxNOW)) {
        spin_unlock_irqrestore(&priv->lock,flags);
        printk (KERN_WARNING "%s: Transmit buffer not free!/n",dev->name);
        dev->stats.tx_errors++;
        txlen = 0;
        /* store skb and send it in interrupt handler */
        return (1);
    }
    write_Frame (dev,skb);
    spin_unlock_irqrestore(&priv->lock,flags);

#ifdef DEBUG
//    dump_packet (dev,skb,"send");
#endif    /* #ifdef DEBUG */

    dev->trans_start = jiffies;

    dev_kfree_skb (skb);
/*    udelay(2000);
    while(status=read_ISQ(dev))
        printk("the txevent :%x",status);
    printk("the txevent :%x",read_cs8900(dev,PP_TxEvent));

//    priv->txlen = skb->len;
//    printk ("Packegt tranlated...");
*/    return (0);
}

static int
cs8900_stop(struct net_device *dev)
{
    /* disable ethernet controller */
    write_cs8900 (dev,PP_BusCTL,0);
    write_cs8900 (dev,PP_TestCTL,0);
    write_cs8900 (dev,PP_SelfCTL,0);
    write_cs8900 (dev,PP_LineCTL,0);
    write_cs8900 (dev,PP_BufCFG,0);
    write_cs8900 (dev,PP_TxCFG,0);
    write_cs8900 (dev,PP_RxCTL,0);
    write_cs8900 (dev,PP_RxCFG,0);

    /* uninstall interrupt handler */
    free_irq (dev->irq,dev);

    /* stop the queue */
    netif_stop_queue (dev);

    return (0);
}

static void cs8900_set_receive_mode (struct net_device *dev)
{
    if ((dev->flags & IFF_PROMISC))
        write_cs8900 (dev,PP_RxCTL,read_cs8900(dev,PP_RxCTL)|PromiscuousA);
    else
        write_cs8900 (dev,PP_RxCTL,read_cs8900(dev,PP_RxCTL)&~PromiscuousA);

    if ((dev->flags & IFF_ALLMULTI) && dev->mc_list)
        write_cs8900 (dev,PP_RxCTL,read_cs8900(dev,PP_RxCTL)|MulticastA);
    else
        write_cs8900 (dev,PP_RxCTL,read_cs8900(dev,PP_RxCTL)&~MulticastA);
}
static struct net_device_ops cs8900_ops = {
    .ndo_open        = &cs8900_open,
    .ndo_start_xmit     = &cs8900_start_xmit,
    .ndo_tx_timeout          = &cs8900_timeout,
    .ndo_set_multicast_list  = &cs8900_set_receive_mode,
    .ndo_stop        = &cs8900_stop,
    .ndo_get_stats           = &cs8900_get_stats,
};

static int __devinit
cs8900_probe(struct platform_device *pdev)
{
    struct cs8900_t *priv;
    int i,ret;
    u16 value;
    struct net_device *ndev;
    int iosize;

    ndev = alloc_etherdev(sizeof(struct cs8900_t));
    if (!ndev) {
        dev_err(&pdev->dev, "no memory for state/n");
        return -ENOMEM;
    }

    SET_NETDEV_DEV(ndev, &pdev->dev);
   
    priv = netdev_priv(ndev);
    memset(priv, 0, sizeof(*priv));

    priv->dev = &pdev->dev;
    priv->ndev = ndev;

    spin_lock_init(&priv->lock);
    mutex_init(&priv->addr_lock);
   
    priv->io_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    if (priv->io_res == NULL){
        dev_err(&pdev->dev, "insufficient resources/n");
        ret = -ENOENT;
        goto err_ioarea;
    }

    iosize = res_size(priv->io_res);
    priv->addr_req = request_mem_region(priv->io_res->start, iosize,pdev->name);
    if (priv->addr_req == NULL) {
        dev_err(&pdev->dev, "cannot request IO/n");
        ret = -ENXIO;
         goto err_iomap;
    }

    priv->io_addr = ioremap(priv->io_res->start, (priv->io_res->end - priv->io_res->start)+1);
    if (priv->io_res == NULL) {
        dev_err(&pdev->dev, "cannot map IO/n");
        ret = -ENXIO;
        goto err_iomap;
    }
   
    priv->ndev->base_addr = (unsigned long)priv->io_addr;
    priv->ndev->irq    = platform_get_irq(pdev, 0);
    priv->irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
    priv->flags = priv->irq_res->flags;
    //test if the chip is cs8900A
    if ((value = read_cs8900 (priv->ndev,PP_ProductID)) != EISA_REG_CODE) {
        printk (KERN_ERR "%s: incorrect signature 0x%.4x/n",pdev->name,value);
        return (-ENXIO);
    }
    printk (KERN_ERR "%s: signature 0x%.4x/n",pdev->name,value);
    /* verify chip version */
    value = read_cs8900 (priv->ndev,PP_ProductID + 2);
    if (VERSION (value) != CS8900A) {
        printk (KERN_ERR "%s: unknown chip version 0x%.8x/n",pdev->name,VERSION (value));
        return (-ENXIO);
    }
 
    priv->irq_res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
    if (priv->irq_res == NULL){
        dev_err(&pdev->dev, "insufficient resources/n");
        ret = -ENOENT;
        goto err_ioarea;
    }

    iosize = res_size(priv->irq_res);
    priv->irq_addr = request_mem_region(priv->irq_res->start, iosize,pdev->name);
    if (priv->irq_addr == NULL) {
        dev_err(&pdev->dev, "cannot request IO/n");
        ret = -ENXIO;
         goto err_iomap;
    }

    priv->io_addr = ioremap(priv->io_res->start, (priv->io_res->end - priv->io_res->start)+1);
    if (priv->io_res == NULL) {
        dev_err(&pdev->dev, "cannot map IO/n");
        ret = -ENXIO;
        goto err_iomap;
    }

    ether_setup(priv->ndev);
    priv->ndev->netdev_ops = &cs8900_ops;
    priv->ndev->watchdog_timeo     = msecs_to_jiffies(5000);
    priv->ndev->watchdog_timeo     = HZ;

    priv->ndev->dev_addr[0] = MAC_Addr[0];
    priv->ndev->dev_addr[1] = MAC_Addr[1];
    priv->ndev->dev_addr[2] = MAC_Addr[2];
    priv->ndev->dev_addr[3] = MAC_Addr[3];
    priv->ndev->dev_addr[4] = MAC_Addr[4];
    priv->ndev->dev_addr[5] = MAC_Addr[5];

    platform_set_drvdata(pdev, priv->ndev);
    ret = register_netdev(priv->ndev);
    if (ret == 0)
        printk(KERN_INFO "cs8900 driver register succeed/n");
/*    write_cs8900 (ndev,PP_SelfCTL,read_cs8900(ndev,PP_RxCTL)|RESET);

    while(!read_cs8900(ndev,PP_SelfST)&INITD);            //wait for reset complete
*/
    write_cs8900 (priv->ndev,PP_IntNum,0);
    for (i = 0; i < ETH_ALEN; i += 2)
        write_cs8900 (ndev,PP_IA + i,ndev->dev_addr[i] | (ndev->dev_addr[i + 1] << 8));
    return (0);
    //device_create_file(struct device *device, struct device_attribute *entry);
 err_iomap:
    iounmap(priv->io_addr);

 err_ioarea:
    release_resource(priv->io_res);
    kfree(priv->io_res);
    return ret;
}
static int __devexit
cs8900_drv_remove(struct platform_device *pdev)
{
    struct net_device *ndev = platform_get_drvdata(pdev);

    platform_set_drvdata(pdev, NULL);

    unregister_netdev(ndev);
    free_netdev(ndev);        /* free device structure */

    return 0;
}

static int
cs8900_drv_suspend(struct platform_device *dev, pm_message_t state)
{
    return 0;
}

static int
cs8900_drv_resume(struct platform_device *dev)
{
    return 0;
}


static struct platform_driver cs8900_driver = {
    .driver    = {
        .name    = "cs8900",
        .owner     = THIS_MODULE,
    },
    .probe   = cs8900_probe,
    .remove  = __devexit_p(cs8900_drv_remove),
    .suspend = cs8900_drv_suspend,
    .resume  = cs8900_drv_resume,
};
#define S3C2410_CPUIRQ_OFFSET     (16)
#define S3C2410_IRQ(x) ((x) + S3C2410_CPUIRQ_OFFSET)
#define IRQ_EINT9      S3C2410_IRQ(37)

static struct resource s3c_eth0_resource[] = {
    [0] = {
        .start    = 0x19000000,
        .end    = 0x19000000 + 16,
        .flags    = IORESOURCE_MEM,
    },
    [1] = {
        .start    = 0x56000000,
        .end    = 0x56000000 + 256,
        .flags    = IORESOURCE_MEM,
    },
    [2] = {
        .start    = IRQ_EINT9,
        .end    = IRQ_EINT9,
        .flags    = IORESOURCE_IRQ|IRQF_TRIGGER_HIGH,
    },
};

struct platform_device s3c_device_eth0 = {
    .name          = "cs8900",
    .id          = -1,
    .num_resources      = ARRAY_SIZE(s3c_eth0_resource),
    .resource      = s3c_eth0_resource,
};

static struct platform_device __initdata *smdk_eths[] = {
    &s3c_device_eth0,
};

static int __init
cs8900_init(void)
{
    printk(KERN_INFO "%s Ethernet Driver, V%s/n", CARDNAME, DRV_VERSION);
    platform_add_devices(smdk_eths, ARRAY_SIZE(smdk_eths));
    return platform_driver_register(&cs8900_driver);
}

static void __exit
cs8900_cleanup(void)
{
    platform_driver_unregister(&cs8900_driver);
}

MODULE_AUTHOR ("Abraham van der Merwe <abraham at 2d3d.co.za>");
MODULE_DESCRIPTION (VERSION_STRING);
MODULE_LICENSE ("GPL");

module_init (cs8900_init);
module_exit (cs8900_cleanup);

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值