patch-2_6_7-vs1_9_1_12
[linux-2.6.git] / arch / cris / arch-v10 / drivers / ethernet.c
index 943db0b..f258c00 100644 (file)
@@ -1,4 +1,4 @@
-/* $Id: ethernet.c,v 1.17 2003/07/04 08:27:37 starvik Exp $
+/* $Id: ethernet.c,v 1.22 2004/05/14 07:58:03 starvik Exp $
  *
  * e100net.c: A network driver for the ETRAX 100LX network controller.
  *
@@ -7,6 +7,15 @@
  * The outline of this driver comes from skeleton.c.
  *
  * $Log: ethernet.c,v $
+ * Revision 1.22  2004/05/14 07:58:03  starvik
+ * Merge of changes from 2.4
+ *
+ * Revision 1.20  2004/03/11 11:38:40  starvik
+ * Merge of Linux 2.6.4
+ *
+ * Revision 1.18  2003/12/03 13:45:46  starvik
+ * Use hardware pad for short packets to prevent information leakage.
+ *
  * Revision 1.17  2003/07/04 08:27:37  starvik
  * Merge of Linux 2.5.74
  *
@@ -258,6 +267,16 @@ typedef struct etrax_eth_descr
        struct sk_buff* skb;
 } etrax_eth_descr;
 
+/* Some transceivers requires special handling */
+struct transceiver_ops
+{
+       unsigned int oui;
+       void (*check_speed)(void);
+       void (*check_duplex)(void);
+};
+
+struct transceiver_ops* transceiver;
+
 /* Duplex settings */
 enum duplex
 {
@@ -278,10 +297,17 @@ enum duplex
 */
 #define MDIO_BASE_STATUS_REG                0x1
 #define MDIO_BASE_CONTROL_REG               0x0
+#define MDIO_PHY_ID_HIGH_REG                0x2
+#define MDIO_PHY_ID_LOW_REG                 0x3
 #define MDIO_BC_NEGOTIATE                0x0200
 #define MDIO_BC_FULL_DUPLEX_MASK         0x0100
 #define MDIO_BC_AUTO_NEG_MASK            0x1000
 #define MDIO_BC_SPEED_SELECT_MASK        0x2000
+#define MDIO_STATUS_100_FD               0x4000
+#define MDIO_STATUS_100_HD               0x2000
+#define MDIO_STATUS_10_FD                0x1000
+#define MDIO_STATUS_10_HD                0x0800
+#define MDIO_STATUS_SPEED_DUPLEX_MASK   0x7800
 #define MDIO_ADVERTISMENT_REG               0x4
 #define MDIO_ADVERT_100_FD                0x100
 #define MDIO_ADVERT_100_HD                0x080
@@ -295,9 +321,13 @@ enum duplex
 
 /* Broadcom specific */
 #define MDIO_AUX_CTRL_STATUS_REG           0x18
-#define MDIO_FULL_DUPLEX_IND                0x1
-#define MDIO_SPEED                          0x2
-#define MDIO_PHYS_ADDR                      0x0
+#define MDIO_BC_FULL_DUPLEX_IND             0x1
+#define MDIO_BC_SPEED                       0x2
+
+/* TDK specific */
+#define MDIO_TDK_DIAGNOSTIC_REG              18
+#define MDIO_TDK_DIAGNOSTIC_RATE          0x400
+#define MDIO_TDK_DIAGNOSTIC_DPLX          0x800
 
 /* Network flash constants */
 #define NET_FLASH_TIME                  (HZ/50) /* 20 ms */
@@ -341,6 +371,9 @@ static etrax_eth_descr* myNextTxDesc;  /* Next descriptor to use */
 static etrax_eth_descr TxDescList[NBR_OF_TX_DESC] __attribute__ ((aligned(32)));
 
 static unsigned int network_rec_config_shadow = 0;
+static unsigned int mdio_phy_addr; /* Transciever address */
+
+static unsigned int network_tr_ctrl_shadow = 0;
 
 /* Network speed indication. */
 static struct timer_list speed_timer = TIMER_INITIALIZER(NULL, 0, 0);
@@ -376,6 +409,7 @@ static void set_multicast_list(struct net_device *dev);
 static void e100_hardware_send_packet(char *buf, int length);
 static void update_rx_stats(struct net_device_stats *);
 static void update_tx_stats(struct net_device_stats *);
+static int e100_probe_transceiver(void);
 
 static void e100_check_speed(unsigned long dummy);
 static void e100_set_speed(unsigned long speed);
@@ -393,6 +427,21 @@ static void e100_reset_transceiver(void);
 static void e100_clear_network_leds(unsigned long dummy);
 static void e100_set_network_leds(int active);
 
+static void broadcom_check_speed(void);
+static void broadcom_check_duplex(void);
+static void tdk_check_speed(void);
+static void tdk_check_duplex(void);
+static void generic_check_speed(void);
+static void generic_check_duplex(void);
+
+struct transceiver_ops transceivers[] =
+{
+       {0x1018, broadcom_check_speed, broadcom_check_duplex},  /* Broadcom */
+       {0xC039, tdk_check_speed, tdk_check_duplex},            /* TDK 2120 */
+       {0x039C, tdk_check_speed, tdk_check_duplex},            /* TDK 2120C */
+       {0x0000, generic_check_speed, generic_check_duplex}     /* Generic, must be last */
+};
+
 #define tx_done(dev) (*R_DMA_CH0_CMD == 0)
 
 /*
@@ -409,7 +458,8 @@ etrax_ethernet_init(void)
        struct net_device *dev;
        int i, err;
 
-       printk("ETRAX 100LX 10/100MBit ethernet v2.0 (c) 2000-2003 Axis Communications AB\n");
+       printk(KERN_INFO
+              "ETRAX 100LX 10/100MBit ethernet v2.0 (c) 2000-2003 Axis Communications AB\n");
 
        dev = alloc_etherdev(sizeof(struct net_local));
        if (!dev)
@@ -496,7 +546,6 @@ etrax_ethernet_init(void)
        current_speed_selection = 0; /* Auto */
        speed_timer.expires = jiffies + NET_LINK_UP_CHECK_INTERVAL;
        speed_timer.function = e100_check_speed;
-       add_timer(&speed_timer);
         
        clear_led_timer.function = e100_clear_network_leds;
         
@@ -504,7 +553,6 @@ etrax_ethernet_init(void)
        current_duplex = autoneg;
        duplex_timer.expires = jiffies + NET_DUPLEX_CHECK_INTERVAL;             
        duplex_timer.function = e100_check_duplex;
-       add_timer(&duplex_timer);
 
        /* Initialize group address registers to make sure that no */
        /* unwanted addresses are matched */
@@ -543,7 +591,7 @@ e100_set_mac_address(struct net_device *dev, void *p)
 
        /* show it in the log as well */
 
-       printk("%s: changed MAC to ", dev->name);
+       printk(KERN_INFO "%s: changed MAC to ", dev->name);
 
        for (i = 0; i < 5; i++)
                printk("%02X:", dev->dev_addr[i]);
@@ -569,12 +617,6 @@ e100_open(struct net_device *dev)
 {
        unsigned long flags;
 
-       /* disable the ethernet interface while we configure it */
-
-       *R_NETWORK_GEN_CONFIG =
-               IO_STATE(R_NETWORK_GEN_CONFIG, phy,    mii_clk) |
-               IO_STATE(R_NETWORK_GEN_CONFIG, enable, off);
-
        /* enable the MDIO output pin */
 
        *R_NETWORK_MGM_CTRL = IO_STATE(R_NETWORK_MGM_CTRL, mdoe, enable);
@@ -645,14 +687,14 @@ e100_open(struct net_device *dev)
                IO_STATE(R_NETWORK_GEN_CONFIG, phy,    mii_clk) |
                IO_STATE(R_NETWORK_GEN_CONFIG, enable, on);
 
-       *R_NETWORK_TR_CTRL = 
-               IO_STATE(R_NETWORK_TR_CTRL, clr_error, clr) |
-               IO_STATE(R_NETWORK_TR_CTRL, delay, none) |
-               IO_STATE(R_NETWORK_TR_CTRL, cancel, dont) |
-               IO_STATE(R_NETWORK_TR_CTRL, cd, enable) |
-               IO_STATE(R_NETWORK_TR_CTRL, retry, enable) |
-               IO_STATE(R_NETWORK_TR_CTRL, pad, enable) |
-               IO_STATE(R_NETWORK_TR_CTRL, crc, enable);
+       SETS(network_tr_ctrl_shadow, R_NETWORK_TR_CTRL, clr_error, clr);
+       SETS(network_tr_ctrl_shadow, R_NETWORK_TR_CTRL, delay, none);
+       SETS(network_tr_ctrl_shadow, R_NETWORK_TR_CTRL, cancel, dont);
+       SETS(network_tr_ctrl_shadow, R_NETWORK_TR_CTRL, cd, enable);
+       SETS(network_tr_ctrl_shadow, R_NETWORK_TR_CTRL, retry, enable);
+       SETS(network_tr_ctrl_shadow, R_NETWORK_TR_CTRL, pad, enable);
+       SETS(network_tr_ctrl_shadow, R_NETWORK_TR_CTRL, crc, enable);
+       *R_NETWORK_TR_CTRL = network_tr_ctrl_shadow;
 
        save_flags(flags);
        cli();
@@ -660,7 +702,8 @@ e100_open(struct net_device *dev)
        /* enable the irq's for ethernet DMA */
 
        *R_IRQ_MASK2_SET =
-               IO_STATE(R_IRQ_MASK2_SET, dma1_eop, set); 
+               IO_STATE(R_IRQ_MASK2_SET, dma0_eop, set) |
+               IO_STATE(R_IRQ_MASK2_SET, dma1_eop, set);
 
        *R_IRQ_MASK0_SET =
                IO_STATE(R_IRQ_MASK0_SET, overrun,       set) |
@@ -689,6 +732,14 @@ e100_open(struct net_device *dev)
 
        restore_flags(flags);
        
+       /* Probe for transceiver */
+       if (e100_probe_transceiver())
+               goto grace_exit3;
+
+       /* Start duplex/speed timers */
+       add_timer(&speed_timer);
+       add_timer(&duplex_timer);
+
        /* We are now ready to accept transmit requeusts from
         * the queueing layer of the networking.
         */
@@ -696,6 +747,8 @@ e100_open(struct net_device *dev)
 
        return 0;
 
+grace_exit3:
+       free_irq(NETWORK_STATUS_IRQ_NBR, (void *)dev);
 grace_exit2:
        free_irq(NETWORK_DMA_TX_IRQ_NBR, (void *)dev);
 grace_exit1:
@@ -705,9 +758,38 @@ grace_exit0:
 }
 
 
+static void
+generic_check_speed(void)
+{
+       unsigned long data;
+       data = e100_get_mdio_reg(MDIO_ADVERTISMENT_REG);
+       if ((data & MDIO_ADVERT_100_FD) ||
+           (data & MDIO_ADVERT_100_HD))
+               current_speed = 100;
+       else
+               current_speed = 10;
+}
+
+static void
+tdk_check_speed(void)
+{
+       unsigned long data;
+       data = e100_get_mdio_reg(MDIO_TDK_DIAGNOSTIC_REG);
+       current_speed = (data & MDIO_TDK_DIAGNOSTIC_RATE ? 100 : 10);
+}
+
+static void
+broadcom_check_speed(void)
+{
+       unsigned long data;
+       data = e100_get_mdio_reg(MDIO_AUX_CTRL_STATUS_REG);
+       current_speed = (data & MDIO_BC_SPEED ? 100 : 10);
+}
+
 static void
 e100_check_speed(unsigned long dummy)
 {
+       static int led_initiated = 0;
        unsigned long data;
        int old_speed = current_speed;
 
@@ -715,12 +797,13 @@ e100_check_speed(unsigned long dummy)
        if (!(data & MDIO_LINK_UP_MASK)) {
                current_speed = 0;
        } else {
-               data = e100_get_mdio_reg(MDIO_AUX_CTRL_STATUS_REG);
-               current_speed = (data & MDIO_SPEED ? 100 : 10);
+               transceiver->check_speed();
        }
        
-       if (old_speed != current_speed)
+       if ((old_speed != current_speed) || !led_initiated) {
+               led_initiated = 1;
                e100_set_network_leds(NO_NETWORK_ACTIVITY);
+       }
 
        /* Reinitialize the timer. */
        speed_timer.expires = jiffies + NET_LINK_UP_CHECK_INTERVAL;
@@ -781,29 +864,21 @@ e100_negotiate(void)
 static void
 e100_set_speed(unsigned long speed)
 {
-       current_speed_selection = speed;
-       e100_negotiate();
+       if (speed != current_speed_selection) {
+               current_speed_selection = speed;
+               e100_negotiate();
+       }
 }
 
 static void
 e100_check_duplex(unsigned long dummy)
 {
-       unsigned long data;
-
-       data = e100_get_mdio_reg(MDIO_AUX_CTRL_STATUS_REG);
-        
-       if (data & MDIO_FULL_DUPLEX_IND) {
-               if (!full_duplex) { /* Duplex changed to full? */
-                       full_duplex = 1;
-                       SETF(network_rec_config_shadow, R_NETWORK_REC_CONFIG, duplex, full_duplex);
-                       *R_NETWORK_REC_CONFIG = network_rec_config_shadow;
-               }
-       } else { /* half */
-               if (full_duplex) { /* Duplex changed to half? */
-                       full_duplex = 0;
-                       SETF(network_rec_config_shadow, R_NETWORK_REC_CONFIG, duplex, full_duplex);
-                       *R_NETWORK_REC_CONFIG = network_rec_config_shadow;
-               }
+       int old_duplex = full_duplex;
+       transceiver->check_duplex();
+       if (old_duplex != full_duplex) {
+               /* Duplex changed */
+               SETF(network_rec_config_shadow, R_NETWORK_REC_CONFIG, duplex, full_duplex);
+               *R_NETWORK_REC_CONFIG = network_rec_config_shadow;
        }
 
        /* Reinitialize the timer. */
@@ -811,13 +886,72 @@ e100_check_duplex(unsigned long dummy)
        add_timer(&duplex_timer);
 }
 
+static void
+generic_check_duplex(void)
+{
+       unsigned long data;
+       data = e100_get_mdio_reg(MDIO_ADVERTISMENT_REG);
+       if ((data & MDIO_ADVERT_100_FD) ||
+           (data & MDIO_ADVERT_10_FD))
+               full_duplex = 1;
+       else
+               full_duplex = 0;
+}
+
+static void
+tdk_check_duplex(void)
+{
+       unsigned long data;
+       data = e100_get_mdio_reg(MDIO_TDK_DIAGNOSTIC_REG);
+       full_duplex = (data & MDIO_TDK_DIAGNOSTIC_DPLX) ? 1 : 0;
+}
+
+static void
+broadcom_check_duplex(void)
+{
+       unsigned long data;
+       data = e100_get_mdio_reg(MDIO_AUX_CTRL_STATUS_REG);
+       full_duplex = (data & MDIO_BC_FULL_DUPLEX_IND) ? 1 : 0;
+}
+
 static void 
 e100_set_duplex(enum duplex new_duplex)
 {
-       current_duplex = new_duplex;
-       e100_negotiate();
+       if (new_duplex != current_duplex) {
+               current_duplex = new_duplex;
+               e100_negotiate();
+       }
 }
 
+static int
+e100_probe_transceiver(void)
+{
+       unsigned int phyid_high;
+       unsigned int phyid_low;
+       unsigned int oui;
+       struct transceiver_ops* ops = NULL;
+
+       /* Probe MDIO physical address */
+       for (mdio_phy_addr = 0; mdio_phy_addr <= 31; mdio_phy_addr++) {
+               if (e100_get_mdio_reg(MDIO_BASE_STATUS_REG) != 0xffff)
+                       break;
+       }
+       if (mdio_phy_addr == 32)
+                return -ENODEV;
+
+       /* Get manufacturer */
+       phyid_high = e100_get_mdio_reg(MDIO_PHY_ID_HIGH_REG);
+       phyid_low = e100_get_mdio_reg(MDIO_PHY_ID_LOW_REG);
+       oui = (phyid_high << 6) | (phyid_low >> 10);
+
+       for (ops = &transceivers[0]; ops->oui; ops++) {
+               if (ops->oui == oui)
+                       break;
+       }
+       transceiver = ops;
+
+       return 0;
+}
 
 static unsigned short
 e100_get_mdio_reg(unsigned char reg_num)
@@ -827,7 +961,7 @@ e100_get_mdio_reg(unsigned char reg_num)
        int bitCounter;
        
        /* Start of frame, OP Code, Physical Address, Register Address */
-       cmd = (MDIO_START << 14) | (MDIO_READ << 12) | (MDIO_PHYS_ADDR << 7) |
+       cmd = (MDIO_START << 14) | (MDIO_READ << 12) | (mdio_phy_addr << 7) |
                (reg_num << 2);
        
        e100_send_mdio_cmd(cmd, 0);
@@ -848,7 +982,7 @@ e100_set_mdio_reg(unsigned char reg, unsigned short data)
        int bitCounter;
        unsigned short cmd;
 
-       cmd = (MDIO_START << 14) | (MDIO_WRITE << 12) | (MDIO_PHYS_ADDR << 7) |
+       cmd = (MDIO_START << 14) | (MDIO_WRITE << 12) | (mdio_phy_addr << 7) |
              (reg << 2);
 
        e100_send_mdio_cmd(cmd, 1);
@@ -916,7 +1050,7 @@ e100_reset_transceiver(void)
 
        data = e100_get_mdio_reg(MDIO_BASE_CONTROL_REG);
 
-       cmd = (MDIO_START << 14) | (MDIO_WRITE << 12) | (MDIO_PHYS_ADDR << 7) | (MDIO_BASE_CONTROL_REG << 2);
+       cmd = (MDIO_START << 14) | (MDIO_WRITE << 12) | (mdio_phy_addr << 7) | (MDIO_BASE_CONTROL_REG << 2);
 
        e100_send_mdio_cmd(cmd, 1);
        
@@ -984,7 +1118,6 @@ static int
 e100_send_packet(struct sk_buff *skb, struct net_device *dev)
 {
        struct net_local *np = (struct net_local *)dev->priv;
-       int length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
        unsigned char *buf = skb->data;
        unsigned long flags;
        
@@ -997,15 +1130,12 @@ e100_send_packet(struct sk_buff *skb, struct net_device *dev)
 
        dev->trans_start = jiffies;
        
-       e100_hardware_send_packet(buf, length);
+       e100_hardware_send_packet(buf, skb->len);
 
        myNextTxDesc = phys_to_virt(myNextTxDesc->descr.next);
 
        /* Stop queue if full */
        if (myNextTxDesc == myFirstTxDesc) {
-               /* Enable transmit interrupt to wake up queue */
-               *R_DMA_CH0_CLR_INTR = IO_STATE(R_DMA_CH0_CLR_INTR, clr_eop, do);                
-               *R_IRQ_MASK2_SET = IO_STATE(R_IRQ_MASK2_SET, dma0_eop, set);
                netif_stop_queue(dev);
        }
 
@@ -1026,6 +1156,11 @@ e100rxtx_interrupt(int irq, void *dev_id, struct pt_regs * regs)
        struct net_local *np = (struct net_local *)dev->priv;
        unsigned long irqbits = *R_IRQ_MASK2_RD;
  
+       /* Disable RX/TX IRQs to avoid reentrancy */
+       *R_IRQ_MASK2_CLR =
+         IO_STATE(R_IRQ_MASK2_CLR, dma0_eop, clr) |
+         IO_STATE(R_IRQ_MASK2_CLR, dma1_eop, clr);
+
        /* Handle received packets */
        if (irqbits & IO_STATE(R_IRQ_MASK2_RD, dma1_eop, active)) {
                /* acknowledge the eop interrupt */
@@ -1069,9 +1204,14 @@ e100rxtx_interrupt(int irq, void *dev_id, struct pt_regs * regs)
        if (irqbits & IO_STATE(R_IRQ_MASK2_RD, dma0_eop, active)) {
                /* acknowledge the eop interrupt and wake up queue */
                *R_DMA_CH0_CLR_INTR = IO_STATE(R_DMA_CH0_CLR_INTR, clr_eop, do);
-               *R_IRQ_MASK2_CLR = IO_STATE(R_IRQ_MASK2_CLR, dma0_eop, clr);            
                netif_wake_queue(dev);
        }
+
+       /* Enable RX/TX IRQs again */
+       *R_IRQ_MASK2_SET =
+         IO_STATE(R_IRQ_MASK2_SET, dma0_eop, set) |
+         IO_STATE(R_IRQ_MASK2_SET, dma1_eop, set);
+
        return IRQ_HANDLED;
 }
 
@@ -1084,7 +1224,9 @@ e100nw_interrupt(int irq, void *dev_id, struct pt_regs * regs)
 
        /* check for underrun irq */
        if (irqbits & IO_STATE(R_IRQ_MASK0_RD, underrun, active)) { 
-               *R_NETWORK_TR_CTRL = IO_STATE(R_NETWORK_TR_CTRL, clr_error, clr);
+               SETS(network_tr_ctrl_shadow, R_NETWORK_TR_CTRL, clr_error, clr);
+               *R_NETWORK_TR_CTRL = network_tr_ctrl_shadow;
+               SETS(network_tr_ctrl_shadow, R_NETWORK_TR_CTRL, clr_error, nop);
                np->stats.tx_errors++;
                D(printk("ethernet receiver underrun!\n"));
        }
@@ -1096,6 +1238,9 @@ e100nw_interrupt(int irq, void *dev_id, struct pt_regs * regs)
        }
        /* check for excessive collision irq */
        if (irqbits & IO_STATE(R_IRQ_MASK0_RD, excessive_col, active)) { 
+               SETS(network_tr_ctrl_shadow, R_NETWORK_TR_CTRL, clr_error, clr);
+               *R_NETWORK_TR_CTRL = network_tr_ctrl_shadow;
+               SETS(network_tr_ctrl_shadow, R_NETWORK_TR_CTRL, clr_error, nop);
                *R_NETWORK_TR_CTRL = IO_STATE(R_NETWORK_TR_CTRL, clr_error, clr);
                np->stats.tx_errors++;
                D(printk("ethernet excessive collisions!\n"));
@@ -1210,14 +1355,10 @@ e100_close(struct net_device *dev)
 {
        struct net_local *np = (struct net_local *)dev->priv;
 
-       printk("Closing %s.\n", dev->name);
+       printk(KERN_INFO "Closing %s.\n", dev->name);
 
        netif_stop_queue(dev);
 
-       *R_NETWORK_GEN_CONFIG =
-               IO_STATE(R_NETWORK_GEN_CONFIG, phy,    mii_clk) |
-               IO_STATE(R_NETWORK_GEN_CONFIG, enable, off);
-       
        *R_IRQ_MASK0_CLR =
                IO_STATE(R_IRQ_MASK0_CLR, overrun, clr) |
                IO_STATE(R_IRQ_MASK0_CLR, underrun, clr) |
@@ -1245,21 +1386,25 @@ e100_close(struct net_device *dev)
        update_rx_stats(&np->stats);
        update_tx_stats(&np->stats);
 
+       /* Stop speed/duplex timers */
+       del_timer(&speed_timer);
+       del_timer(&duplex_timer);
+
        return 0;
 }
 
 static int
 e100_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
 {
-       struct mii_ioctl_data *data = (struct mii_ioctl_data *)&ifr->ifr_data;
-       struct net_local *np = (struct net_local *)dev->priv;
+       struct mii_ioctl_data *data = if_mii(ifr);
+       struct net_local *np = netdev_priv(dev);
 
        spin_lock(&np->lock); /* Preempt protection */
        switch (cmd) {
                case SIOCETHTOOL:
                        return e100_ethtool_ioctl(dev,ifr);
                case SIOCGMIIPHY: /* Get PHY address */
-                       data->phy_id = MDIO_PHYS_ADDR;
+                       data->phy_id = mdio_phy_addr;
                        break;
                case SIOCGMIIREG: /* Read MII register */
                        data->val_out = e100_get_mdio_reg(data->reg_num);
@@ -1278,7 +1423,7 @@ e100_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
                case SET_ETH_SPEED_AUTO:              /* Auto negotiate speed */
                        e100_set_speed(0);
                        break;
-               case SET_ETH_DUPLEX_HALF:              /* Hhalf duplex. */
+               case SET_ETH_DUPLEX_HALF:              /* Half duplex. */
                        e100_set_duplex(half);
                        break;
                case SET_ETH_DUPLEX_FULL:              /* Full duplex. */
@@ -1312,12 +1457,12 @@ e100_ethtool_ioctl(struct net_device *dev, struct ifreq *ifr)
                          SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full;
                        ecmd.port = PORT_TP;
                        ecmd.transceiver = XCVR_EXTERNAL;
-                       ecmd.phy_address = MDIO_PHYS_ADDR;
+                       ecmd.phy_address = mdio_phy_addr;
                        ecmd.speed = current_speed;
                        ecmd.duplex = full_duplex ? DUPLEX_FULL : DUPLEX_HALF;
                        ecmd.advertising = ADVERTISED_TP;
                        if (current_duplex == autoneg && current_speed_selection == 0)
-                               ecmd.advertising = ADVERTISED_Autoneg;
+                               ecmd.advertising |= ADVERTISED_Autoneg;
                        else {
                                ecmd.advertising |= 
                                  ADVERTISED_10baseT_Half | ADVERTISED_10baseT_Full |
@@ -1355,7 +1500,7 @@ e100_ethtool_ioctl(struct net_device *dev, struct ifreq *ifr)
                        struct ethtool_drvinfo info;
                        memset((void *) &info, 0, sizeof (info));
                        strncpy(info.driver, "ETRAX 100LX", sizeof(info.driver) - 1);
-                       strncpy(info.version, "$Revision: 1.17 $", sizeof(info.version) - 1);
+                       strncpy(info.version, "$Revision: 1.22 $", sizeof(info.version) - 1);
                        strncpy(info.fw_version, "N/A", sizeof(info.fw_version) - 1);
                        strncpy(info.bus_info, "N/A", sizeof(info.bus_info) - 1);
                        info.regdump_len = 0;
@@ -1595,7 +1740,11 @@ e100_set_network_leds(int active)
 
        if (!current_speed) {
                /* Make LED red, link is down */
+#if defined(CONFIG_ETRAX_NETWORK_RED_ON_NO_CONNECTION)
+               LED_NETWORK_SET(LED_RED);
+#else
                LED_NETWORK_SET(LED_OFF);
+#endif
        }
        else if (light_leds) {
                if (current_speed == 10) {
@@ -1615,4 +1764,26 @@ etrax_init_module(void)
        return etrax_ethernet_init();
 }
 
+static int __init
+e100_boot_setup(char* str)
+{
+       struct sockaddr sa = {0};
+       int i;
+
+       /* Parse the colon separated Ethernet station address */
+       for (i = 0; i <  ETH_ALEN; i++) {
+               unsigned int tmp;
+               if (sscanf(str + 3*i, "%2x", &tmp) != 1) {
+                       printk(KERN_WARNING "Malformed station address");
+                       return 0;
+               }
+               sa.sa_data[i] = (char)tmp;
+       }
+
+       default_mac = sa;
+       return 1;
+}
+
+__setup("etrax100_eth=", e100_boot_setup);
+
 module_init(etrax_init_module);