DM9000: Add initial ethtool support
[pandora-kernel.git] / drivers / net / dm9000.c
index 27ac010..709fd67 100644 (file)
 #include <linux/spinlock.h>
 #include <linux/crc32.h>
 #include <linux/mii.h>
+#include <linux/ethtool.h>
 #include <linux/dm9000.h>
 #include <linux/delay.h>
 #include <linux/platform_device.h>
+#include <linux/irq.h>
 
 #include <asm/delay.h>
 #include <asm/irq.h>
 
 #define CARDNAME "dm9000"
 #define PFX CARDNAME ": "
-
-#define DM9000_TIMER_WUT  jiffies+(HZ*2)       /* timer wakeup time : 2 second */
-
-#define DM9000_DEBUG 0
-
-#if DM9000_DEBUG > 2
-#define PRINTK3(args...)  printk(CARDNAME ": " args)
-#else
-#define PRINTK3(args...)  do { } while(0)
-#endif
-
-#if DM9000_DEBUG > 1
-#define PRINTK2(args...)  printk(CARDNAME ": " args)
-#else
-#define PRINTK2(args...)  do { } while(0)
-#endif
-
-#if DM9000_DEBUG > 0
-#define PRINTK1(args...)  printk(CARDNAME ": " args)
-#define PRINTK(args...)   printk(CARDNAME ": " args)
-#else
-#define PRINTK1(args...)  do { } while(0)
-#define PRINTK(args...)   printk(KERN_DEBUG args)
-#endif
+#define DRV_VERSION    "1.30"
 
 #ifdef CONFIG_BLACKFIN
 #define readsb insb
 #define writesb        outsb
 #define writesw        outsw
 #define writesl        outsl
-#define DM9000_IRQ_FLAGS       (IRQF_SHARED | IRQF_TRIGGER_HIGH)
+#define DEFAULT_TRIGGER IRQF_TRIGGER_HIGH
 #else
-#define DM9000_IRQ_FLAGS       IRQF_SHARED
+#define DEFAULT_TRIGGER (0)
 #endif
 
 /*
@@ -136,18 +115,22 @@ typedef struct board_info {
        u16 dbug_cnt;
        u8 io_mode;             /* 0:word, 2:byte */
        u8 phy_addr;
+       unsigned int flags;
+
+       int debug_level;
 
        void (*inblk)(void __iomem *port, void *data, int length);
        void (*outblk)(void __iomem *port, void *data, int length);
        void (*dumpblk)(void __iomem *port, int length);
 
+       struct device   *dev;        /* parent device */
+
        struct resource *addr_res;   /* resources found */
        struct resource *data_res;
        struct resource *addr_req;   /* resources requested */
        struct resource *data_req;
        struct resource *irq_res;
 
-       struct timer_list timer;
        unsigned char srom[128];
        spinlock_t lock;
 
@@ -155,14 +138,26 @@ typedef struct board_info {
        u32 msg_enable;
 } board_info_t;
 
+/* debug code */
+
+#define dm9000_dbg(db, lev, msg...) do {               \
+       if ((lev) < CONFIG_DM9000_DEBUGLEVEL &&         \
+           (lev) < db->debug_level) {                  \
+               dev_dbg(db->dev, msg);                  \
+       }                                               \
+} while (0)
+
+static inline board_info_t *to_dm9000_board(struct net_device *dev)
+{
+       return dev->priv;
+}
+
 /* function declaration ------------------------------------- */
 static int dm9000_probe(struct platform_device *);
 static int dm9000_open(struct net_device *);
 static int dm9000_start_xmit(struct sk_buff *, struct net_device *);
 static int dm9000_stop(struct net_device *);
 
-
-static void dm9000_timer(unsigned long);
 static void dm9000_init_dm9000(struct net_device *);
 
 static irqreturn_t dm9000_interrupt(int, void *);
@@ -183,7 +178,8 @@ static void program_eeprom(board_info_t * db);
 static void
 dm9000_reset(board_info_t * db)
 {
-       PRINTK1("dm9000x: resetting\n");
+       dev_dbg(db->dev, "resetting device\n");
+
        /* RESET device */
        writeb(DM9000_NCR, db->io_addr);
        udelay(200);
@@ -299,14 +295,10 @@ static void dm9000_set_io(struct board_info *db, int byte_width)
                db->inblk   = dm9000_inblk_8bit;
                break;
 
-       case 2:
-               db->dumpblk = dm9000_dumpblk_16bit;
-               db->outblk  = dm9000_outblk_16bit;
-               db->inblk   = dm9000_inblk_16bit;
-               break;
 
        case 3:
-               printk(KERN_ERR PFX ": 3 byte IO, falling back to 16bit\n");
+               dev_dbg(db->dev, ": 3 byte IO, falling back to 16bit\n");
+       case 2:
                db->dumpblk = dm9000_dumpblk_16bit;
                db->outblk  = dm9000_outblk_16bit;
                db->inblk   = dm9000_inblk_16bit;
@@ -357,6 +349,64 @@ static void dm9000_poll_controller(struct net_device *dev)
 }
 #endif
 
+/* ethtool ops */
+
+static void dm9000_get_drvinfo(struct net_device *dev,
+                              struct ethtool_drvinfo *info)
+{
+       board_info_t *dm = to_dm9000_board(dev);
+
+       strcpy(info->driver, CARDNAME);
+       strcpy(info->version, DRV_VERSION);
+       strcpy(info->bus_info, to_platform_device(dm->dev)->name);
+}
+
+static int dm9000_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+       board_info_t *dm = to_dm9000_board(dev);
+       unsigned long flags;
+
+       spin_lock_irqsave(&dm->lock, flags);
+       mii_ethtool_gset(&dm->mii, cmd);
+       spin_lock_irqsave(&dm->lock, flags);
+
+       return 0;
+}
+
+static int dm9000_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+       board_info_t *dm = to_dm9000_board(dev);
+       unsigned long flags;
+       int rc;
+
+       spin_lock_irqsave(&dm->lock, flags);
+       rc = mii_ethtool_sset(&dm->mii, cmd);
+       spin_lock_irqsave(&dm->lock, flags);
+
+       return rc;
+}
+
+static int dm9000_nway_reset(struct net_device *dev)
+{
+       board_info_t *dm = to_dm9000_board(dev);
+       return mii_nway_restart(&dm->mii);
+}
+
+static u32 dm9000_get_link(struct net_device *dev)
+{
+       board_info_t *dm = to_dm9000_board(dev);
+       return mii_link_ok(&dm->mii);
+}
+
+static const struct ethtool_ops dm9000_ethtool_ops = {
+       .get_drvinfo            = dm9000_get_drvinfo,
+       .get_settings           = dm9000_get_settings,
+       .set_settings           = dm9000_set_settings,
+       .nway_reset             = dm9000_nway_reset,
+       .get_link               = dm9000_get_link,
+};
+
+
 /* dm9000_release_board
  *
  * release a board, and any mapped resources
@@ -409,18 +459,20 @@ dm9000_probe(struct platform_device *pdev)
        /* Init network device */
        ndev = alloc_etherdev(sizeof (struct board_info));
        if (!ndev) {
-               printk("%s: could not allocate device.\n", CARDNAME);
+               dev_err(&pdev->dev, "could not allocate device.\n");
                return -ENOMEM;
        }
 
        SET_NETDEV_DEV(ndev, &pdev->dev);
 
-       PRINTK2("dm9000_probe()");
+       dev_dbg(&pdev->dev, "dm9000_probe()");
 
        /* setup board info structure */
        db = (struct board_info *) ndev->priv;
        memset(db, 0, sizeof (*db));
 
+       db->dev = &pdev->dev;
+
        spin_lock_init(&db->lock);
 
        if (pdev->num_resources < 2) {
@@ -449,7 +501,7 @@ dm9000_probe(struct platform_device *pdev)
 
                if (db->addr_res == NULL || db->data_res == NULL ||
                    db->irq_res == NULL) {
-                       printk(KERN_ERR PFX "insufficient resources\n");
+                       dev_err(db->dev, "insufficient resources\n");
                        ret = -ENOENT;
                        goto out;
                }
@@ -459,7 +511,7 @@ dm9000_probe(struct platform_device *pdev)
                                                  pdev->name);
 
                if (db->addr_req == NULL) {
-                       printk(KERN_ERR PFX "cannot claim address reg area\n");
+                       dev_err(db->dev, "cannot claim address reg area\n");
                        ret = -EIO;
                        goto out;
                }
@@ -467,7 +519,7 @@ dm9000_probe(struct platform_device *pdev)
                db->io_addr = ioremap(db->addr_res->start, i);
 
                if (db->io_addr == NULL) {
-                       printk(KERN_ERR "failed to ioremap address reg\n");
+                       dev_err(db->dev, "failed to ioremap address reg\n");
                        ret = -EINVAL;
                        goto out;
                }
@@ -477,7 +529,7 @@ dm9000_probe(struct platform_device *pdev)
                                                  pdev->name);
 
                if (db->data_req == NULL) {
-                       printk(KERN_ERR PFX "cannot claim data reg area\n");
+                       dev_err(db->dev, "cannot claim data reg area\n");
                        ret = -EIO;
                        goto out;
                }
@@ -485,7 +537,7 @@ dm9000_probe(struct platform_device *pdev)
                db->io_data = ioremap(db->data_res->start, iosize);
 
                if (db->io_data == NULL) {
-                       printk(KERN_ERR "failed to ioremap data reg\n");
+                       dev_err(db->dev,"failed to ioremap data reg\n");
                        ret = -EINVAL;
                        goto out;
                }
@@ -524,6 +576,8 @@ dm9000_probe(struct platform_device *pdev)
 
                if (pdata->dumpblk != NULL)
                        db->dumpblk = pdata->dumpblk;
+
+               db->flags = pdata->flags;
        }
 
        dm9000_reset(db);
@@ -537,12 +591,13 @@ dm9000_probe(struct platform_device *pdev)
 
                if (id_val == DM9000_ID)
                        break;
-               printk("%s: read wrong id 0x%08x\n", CARDNAME, id_val);
+               dev_err(db->dev, "read wrong id 0x%08x\n", id_val);
        }
 
        if (id_val != DM9000_ID) {
-               printk("%s: wrong id: 0x%08x\n", CARDNAME, id_val);
-               goto release;
+               dev_err(db->dev, "wrong id: 0x%08x\n", id_val);
+               ret = -ENODEV;
+               goto out;
        }
 
        /* from this point we assume that we have found a DM9000 */
@@ -556,6 +611,8 @@ dm9000_probe(struct platform_device *pdev)
        ndev->watchdog_timeo = msecs_to_jiffies(watchdog);
        ndev->stop               = &dm9000_stop;
        ndev->set_multicast_list = &dm9000_hash_table;
+       ndev->ethtool_ops        = &dm9000_ethtool_ops;
+
 #ifdef CONFIG_NET_POLL_CONTROLLER
        ndev->poll_controller    = &dm9000_poll_controller;
 #endif
@@ -588,8 +645,8 @@ dm9000_probe(struct platform_device *pdev)
        }
 
        if (!is_valid_ether_addr(ndev->dev_addr))
-               printk("%s: Invalid ethernet MAC address.  Please "
-                      "set using ifconfig\n", ndev->name);
+               dev_warn(db->dev, "%s: Invalid ethernet MAC address. Please "
+                        "set using ifconfig\n", ndev->name);
 
        platform_set_drvdata(pdev, ndev);
        ret = register_netdev(ndev);
@@ -602,9 +659,8 @@ dm9000_probe(struct platform_device *pdev)
        }
        return 0;
 
- release:
- out:
-       printk("%s: not found (%d).\n", CARDNAME, ret);
+out:
+       dev_err(db->dev, "not found (%d).\n", ret);
 
        dm9000_release_board(pdev, db);
        free_netdev(ndev);
@@ -620,10 +676,21 @@ static int
 dm9000_open(struct net_device *dev)
 {
        board_info_t *db = (board_info_t *) dev->priv;
+       unsigned long irqflags = db->irq_res->flags & IRQF_TRIGGER_MASK;
 
-       PRINTK2("entering dm9000_open\n");
+       dev_dbg(db->dev, "entering %s\n", __func__);
 
-       if (request_irq(dev->irq, &dm9000_interrupt, DM9000_IRQ_FLAGS, dev->name, dev))
+       /* If there is no IRQ type specified, default to something that
+        * may work, and tell the user that this is a problem */
+
+       if (irqflags == IRQF_TRIGGER_NONE) {
+               dev_warn(db->dev, "WARNING: no IRQ resource flags set.\n");
+               irqflags = DEFAULT_TRIGGER;
+       }
+       
+       irqflags |= IRQF_SHARED;
+
+       if (request_irq(dev->irq, &dm9000_interrupt, irqflags, dev->name, dev))
                return -EAGAIN;
 
        /* Initialize DM9000 board */
@@ -633,13 +700,6 @@ dm9000_open(struct net_device *dev)
        /* Init driver variable */
        db->dbug_cnt = 0;
 
-       /* set and active a timer process */
-       init_timer(&db->timer);
-       db->timer.expires  = DM9000_TIMER_WUT;
-       db->timer.data     = (unsigned long) dev;
-       db->timer.function = &dm9000_timer;
-       add_timer(&db->timer);
-
        mii_check_media(&db->mii, netif_msg_link(db), 1);
        netif_start_queue(dev);
 
@@ -654,7 +714,7 @@ dm9000_init_dm9000(struct net_device *dev)
 {
        board_info_t *db = (board_info_t *) dev->priv;
 
-       PRINTK1("entering %s\n",__FUNCTION__);
+       dm9000_dbg(db, 1, "entering %s\n", __func__);
 
        /* I/O mode */
        db->io_mode = ior(db, DM9000_ISR) >> 6; /* ISR bit7:6 keeps I/O mode */
@@ -664,6 +724,9 @@ dm9000_init_dm9000(struct net_device *dev)
        iow(db, DM9000_GPCR, GPCR_GEP_CNTL);    /* Let GPIO0 output */
        iow(db, DM9000_GPR, 0); /* Enable PHY */
 
+       if (db->flags & DM9000_PLATF_EXT_PHY)
+               iow(db, DM9000_NCR, NCR_EXT_PHY);
+
        /* Program operating register */
        iow(db, DM9000_TCR, 0);         /* TX Polling clear */
        iow(db, DM9000_BPTR, 0x3f);     /* Less 3Kb, 200us */
@@ -697,7 +760,7 @@ dm9000_start_xmit(struct sk_buff *skb, struct net_device *dev)
        unsigned long flags;
        board_info_t *db = (board_info_t *) dev->priv;
 
-       PRINTK3("dm9000_start_xmit\n");
+       dm9000_dbg(db, 3, "%s:\n", __func__);
 
        if (db->tx_pkt_cnt > 1)
                return 1;
@@ -756,10 +819,7 @@ dm9000_stop(struct net_device *ndev)
 {
        board_info_t *db = (board_info_t *) ndev->priv;
 
-       PRINTK1("entering %s\n",__FUNCTION__);
-
-       /* deleted timer */
-       del_timer(&db->timer);
+       dm9000_dbg(db, 1, "entering %s\n", __func__);
 
        netif_stop_queue(ndev);
        netif_carrier_off(ndev);
@@ -802,19 +862,14 @@ static irqreturn_t
 dm9000_interrupt(int irq, void *dev_id)
 {
        struct net_device *dev = dev_id;
-       board_info_t *db;
+       board_info_t *db = (board_info_t *) dev->priv;
        int int_status;
        u8 reg_save;
 
-       PRINTK3("entering %s\n",__FUNCTION__);
-
-       if (!dev) {
-               PRINTK1("dm9000_interrupt() without DEVICE arg\n");
-               return IRQ_HANDLED;
-       }
+       dm9000_dbg(db, 3, "entering %s\n", __func__);
 
        /* A real interrupt coming */
-       db = (board_info_t *) dev->priv;
+
        spin_lock(&db->lock);
 
        /* Save previous register address */
@@ -846,27 +901,9 @@ dm9000_interrupt(int irq, void *dev_id)
        return IRQ_HANDLED;
 }
 
-/*
- *  A periodic timer routine
- *  Dynamic media sense, allocated Rx buffer...
- */
-static void
-dm9000_timer(unsigned long data)
-{
-       struct net_device *dev = (struct net_device *) data;
-       board_info_t *db = (board_info_t *) dev->priv;
-
-       PRINTK3("dm9000_timer()\n");
-
-       mii_check_media(&db->mii, netif_msg_link(db), 0);
-
-       /* Set timer again */
-       db->timer.expires = DM9000_TIMER_WUT;
-       add_timer(&db->timer);
-}
-
 struct dm9000_rxhdr {
-       u16     RxStatus;
+       u8      RxPktReady;
+       u8      RxStatus;
        u16     RxLen;
 } __attribute__((__packed__));
 
@@ -892,7 +929,7 @@ dm9000_rx(struct net_device *dev)
 
                /* Status check: this byte must be 0 or 1 */
                if (rxbyte > DM9000_PKT_RDY) {
-                       printk("status check failed: %d\n", rxbyte);
+                       dev_warn(db->dev, "status check fail: %d\n", rxbyte);
                        iow(db, DM9000_RCR, 0x00);      /* Stop Device */
                        iow(db, DM9000_ISR, IMR_PAR);   /* Stop INT request */
                        return;
@@ -907,30 +944,30 @@ dm9000_rx(struct net_device *dev)
 
                (db->inblk)(db->io_data, &rxhdr, sizeof(rxhdr));
 
-               RxLen = rxhdr.RxLen;
+               RxLen = le16_to_cpu(rxhdr.RxLen);
 
                /* Packet Status check */
                if (RxLen < 0x40) {
                        GoodPacket = false;
-                       PRINTK1("Bad Packet received (runt)\n");
+                       dev_dbg(db->dev, "Bad Packet received (runt)\n");
                }
 
                if (RxLen > DM9000_PKT_MAX) {
-                       PRINTK1("RST: RX Len:%x\n", RxLen);
+                       dev_dbg(db->dev, "RST: RX Len:%x\n", RxLen);
                }
 
-               if (rxhdr.RxStatus & 0xbf00) {
+               if (rxhdr.RxStatus & 0xbf) {
                        GoodPacket = false;
-                       if (rxhdr.RxStatus & 0x100) {
-                               PRINTK1("fifo error\n");
+                       if (rxhdr.RxStatus & 0x01) {
+                               dev_dbg(db->dev, "fifo error\n");
                                dev->stats.rx_fifo_errors++;
                        }
-                       if (rxhdr.RxStatus & 0x200) {
-                               PRINTK1("crc error\n");
+                       if (rxhdr.RxStatus & 0x02) {
+                               dev_dbg(db->dev, "crc error\n");
                                dev->stats.rx_crc_errors++;
                        }
-                       if (rxhdr.RxStatus & 0x8000) {
-                               PRINTK1("length error\n");
+                       if (rxhdr.RxStatus & 0x80) {
+                               dev_dbg(db->dev, "length error\n");
                                dev->stats.rx_length_errors++;
                        }
                }
@@ -1040,7 +1077,7 @@ dm9000_hash_table(struct net_device *dev)
        u16 i, oft, hash_table[4];
        unsigned long flags;
 
-       PRINTK2("dm9000_hash_table()\n");
+       dm9000_dbg(db, 1, "entering %s\n", __func__);
 
        spin_lock_irqsave(&db->lock,flags);
 
@@ -1179,8 +1216,7 @@ dm9000_drv_remove(struct platform_device *pdev)
        dm9000_release_board(pdev, (board_info_t *) ndev->priv);
        free_netdev(ndev);              /* free device structure */
 
-       PRINTK1("clean_module() exit\n");
-
+       dev_dbg(&pdev->dev, "released and freed device\n");
        return 0;
 }
 
@@ -1198,7 +1234,7 @@ static struct platform_driver dm9000_driver = {
 static int __init
 dm9000_init(void)
 {
-       printk(KERN_INFO "%s Ethernet Driver\n", CARDNAME);
+       printk(KERN_INFO "%s Ethernet Driver, V%s\n", CARDNAME, DRV_VERSION);
 
        return platform_driver_register(&dm9000_driver);        /* search board and register */
 }