Commit 6be76a98 authored by Mikael Starvik's avatar Mikael Starvik Committed by Linus Torvalds

[PATCH] CRIS: ethernet driver

Use generic MII interface.
Signed-off-by: default avatarMikael Starvik <starvik@axis.com>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent a86fd4c6
/* $Id: ethernet.c,v 1.22 2004/05/14 07:58:03 starvik Exp $ /* $Id: ethernet.c,v 1.31 2004/10/18 14:49:03 starvik Exp $
* *
* e100net.c: A network driver for the ETRAX 100LX network controller. * e100net.c: A network driver for the ETRAX 100LX network controller.
* *
...@@ -7,6 +7,27 @@ ...@@ -7,6 +7,27 @@
* The outline of this driver comes from skeleton.c. * The outline of this driver comes from skeleton.c.
* *
* $Log: ethernet.c,v $ * $Log: ethernet.c,v $
* Revision 1.31 2004/10/18 14:49:03 starvik
* Use RX interrupt as random source
*
* Revision 1.30 2004/09/29 10:44:04 starvik
* Enabed MAC-address output again
*
* Revision 1.29 2004/08/24 07:14:05 starvik
* Make use of generic MDIO interface and constants.
*
* Revision 1.28 2004/08/20 09:37:11 starvik
* Added support for Intel LXT972A. Creds to Randy Scarborough.
*
* Revision 1.27 2004/08/16 12:37:22 starvik
* Merge of Linux 2.6.8
*
* Revision 1.25 2004/06/21 10:29:57 starvik
* Merge of Linux 2.6.7
*
* Revision 1.23 2004/06/09 05:29:22 starvik
* Avoid any race where R_DMA_CH1_FIRST is NULL (may trigger cache bug).
*
* Revision 1.22 2004/05/14 07:58:03 starvik * Revision 1.22 2004/05/14 07:58:03 starvik
* Merge of changes from 2.4 * Merge of changes from 2.4
* *
...@@ -222,13 +243,13 @@ ...@@ -222,13 +243,13 @@
#include <linux/etherdevice.h> #include <linux/etherdevice.h>
#include <linux/skbuff.h> #include <linux/skbuff.h>
#include <linux/ethtool.h> #include <linux/ethtool.h>
#include <linux/bitops.h>
#include <asm/arch/svinto.h>/* DMA and register descriptions */ #include <asm/arch/svinto.h>/* DMA and register descriptions */
#include <asm/io.h> /* LED_* I/O functions */ #include <asm/io.h> /* LED_* I/O functions */
#include <asm/irq.h> #include <asm/irq.h>
#include <asm/dma.h> #include <asm/dma.h>
#include <asm/system.h> #include <asm/system.h>
#include <asm/bitops.h>
#include <asm/ethernet.h> #include <asm/ethernet.h>
#include <asm/cache.h> #include <asm/cache.h>
...@@ -252,6 +273,7 @@ static struct sockaddr default_mac = { ...@@ -252,6 +273,7 @@ static struct sockaddr default_mac = {
/* Information that need to be kept for each board. */ /* Information that need to be kept for each board. */
struct net_local { struct net_local {
struct net_device_stats stats; struct net_device_stats stats;
struct mii_if_info mii_if;
/* Tx control lock. This protects the transmit buffer ring /* Tx control lock. This protects the transmit buffer ring
* state along with the "tx full" state of the driver. This * state along with the "tx full" state of the driver. This
...@@ -271,8 +293,8 @@ typedef struct etrax_eth_descr ...@@ -271,8 +293,8 @@ typedef struct etrax_eth_descr
struct transceiver_ops struct transceiver_ops
{ {
unsigned int oui; unsigned int oui;
void (*check_speed)(void); void (*check_speed)(struct net_device* dev);
void (*check_duplex)(void); void (*check_duplex)(struct net_device* dev);
}; };
struct transceiver_ops* transceiver; struct transceiver_ops* transceiver;
...@@ -295,25 +317,6 @@ enum duplex ...@@ -295,25 +317,6 @@ enum duplex
/* /*
** MDIO constants. ** MDIO constants.
*/ */
#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
#define MDIO_ADVERT_10_FD 0x040
#define MDIO_ADVERT_10_HD 0x020
#define MDIO_LINK_UP_MASK 0x4
#define MDIO_START 0x1 #define MDIO_START 0x1
#define MDIO_READ 0x2 #define MDIO_READ 0x2
#define MDIO_WRITE 0x1 #define MDIO_WRITE 0x1
...@@ -329,6 +332,11 @@ enum duplex ...@@ -329,6 +332,11 @@ enum duplex
#define MDIO_TDK_DIAGNOSTIC_RATE 0x400 #define MDIO_TDK_DIAGNOSTIC_RATE 0x400
#define MDIO_TDK_DIAGNOSTIC_DPLX 0x800 #define MDIO_TDK_DIAGNOSTIC_DPLX 0x800
/*Intel LXT972A specific*/
#define MDIO_INT_STATUS_REG_2 0x0011
#define MDIO_INT_FULL_DUPLEX_IND ( 1 << 9 )
#define MDIO_INT_SPEED ( 1 << 14 )
/* Network flash constants */ /* Network flash constants */
#define NET_FLASH_TIME (HZ/50) /* 20 ms */ #define NET_FLASH_TIME (HZ/50) /* 20 ms */
#define NET_FLASH_PAUSE (HZ/100) /* 10 ms */ #define NET_FLASH_PAUSE (HZ/100) /* 10 ms */
...@@ -401,6 +409,7 @@ static irqreturn_t e100nw_interrupt(int irq, void *dev_id, struct pt_regs *regs) ...@@ -401,6 +409,7 @@ static irqreturn_t e100nw_interrupt(int irq, void *dev_id, struct pt_regs *regs)
static void e100_rx(struct net_device *dev); static void e100_rx(struct net_device *dev);
static int e100_close(struct net_device *dev); static int e100_close(struct net_device *dev);
static int e100_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd); static int e100_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd);
static int e100_ethtool_ioctl(struct net_device* dev, struct ifreq *ifr);
static int e100_set_config(struct net_device* dev, struct ifmap* map); static int e100_set_config(struct net_device* dev, struct ifmap* map);
static void e100_tx_timeout(struct net_device *dev); static void e100_tx_timeout(struct net_device *dev);
static struct net_device_stats *e100_get_stats(struct net_device *dev); static struct net_device_stats *e100_get_stats(struct net_device *dev);
...@@ -408,37 +417,40 @@ static void set_multicast_list(struct net_device *dev); ...@@ -408,37 +417,40 @@ static void set_multicast_list(struct net_device *dev);
static void e100_hardware_send_packet(char *buf, int length); static void e100_hardware_send_packet(char *buf, int length);
static void update_rx_stats(struct net_device_stats *); static void update_rx_stats(struct net_device_stats *);
static void update_tx_stats(struct net_device_stats *); static void update_tx_stats(struct net_device_stats *);
static int e100_probe_transceiver(void); static int e100_probe_transceiver(struct net_device* dev);
static struct ethtool_ops ethtool_ops;
static void e100_check_speed(unsigned long priv);
static void e100_set_speed(struct net_device* dev, unsigned long speed);
static void e100_check_duplex(unsigned long priv);
static void e100_set_duplex(struct net_device* dev, enum duplex);
static void e100_negotiate(struct net_device* dev);
static void e100_check_speed(unsigned long dummy); static int e100_get_mdio_reg(struct net_device *dev, int phy_id, int location);
static void e100_set_speed(unsigned long speed); static void e100_set_mdio_reg(struct net_device *dev, int phy_id, int location, int value);
static void e100_check_duplex(unsigned long dummy);
static void e100_set_duplex(enum duplex);
static void e100_negotiate(void);
static unsigned short e100_get_mdio_reg(unsigned char reg_num);
static void e100_set_mdio_reg(unsigned char reg, unsigned short data);
static void e100_send_mdio_cmd(unsigned short cmd, int write_cmd); static void e100_send_mdio_cmd(unsigned short cmd, int write_cmd);
static void e100_send_mdio_bit(unsigned char bit); static void e100_send_mdio_bit(unsigned char bit);
static unsigned char e100_receive_mdio_bit(void); static unsigned char e100_receive_mdio_bit(void);
static void e100_reset_transceiver(void); static void e100_reset_transceiver(struct net_device* net);
static void e100_clear_network_leds(unsigned long dummy); static void e100_clear_network_leds(unsigned long dummy);
static void e100_set_network_leds(int active); static void e100_set_network_leds(int active);
static void broadcom_check_speed(void); static void broadcom_check_speed(struct net_device* dev);
static void broadcom_check_duplex(void); static void broadcom_check_duplex(struct net_device* dev);
static void tdk_check_speed(void); static void tdk_check_speed(struct net_device* dev);
static void tdk_check_duplex(void); static void tdk_check_duplex(struct net_device* dev);
static void generic_check_speed(void); static void intel_check_speed(struct net_device* dev);
static void generic_check_duplex(void); static void intel_check_duplex(struct net_device* dev);
static void generic_check_speed(struct net_device* dev);
static void generic_check_duplex(struct net_device* dev);
struct transceiver_ops transceivers[] = struct transceiver_ops transceivers[] =
{ {
{0x1018, broadcom_check_speed, broadcom_check_duplex}, /* Broadcom */ {0x1018, broadcom_check_speed, broadcom_check_duplex}, /* Broadcom */
{0xC039, tdk_check_speed, tdk_check_duplex}, /* TDK 2120 */ {0xC039, tdk_check_speed, tdk_check_duplex}, /* TDK 2120 */
{0x039C, tdk_check_speed, tdk_check_duplex}, /* TDK 2120C */ {0x039C, tdk_check_speed, tdk_check_duplex}, /* TDK 2120C */
{0x04de, intel_check_speed, intel_check_duplex}, /* Intel LXT972A*/
{0x0000, generic_check_speed, generic_check_duplex} /* Generic, must be last */ {0x0000, generic_check_speed, generic_check_duplex} /* Generic, must be last */
}; };
...@@ -456,12 +468,15 @@ static int __init ...@@ -456,12 +468,15 @@ static int __init
etrax_ethernet_init(void) etrax_ethernet_init(void)
{ {
struct net_device *dev; struct net_device *dev;
struct net_local* np;
int i, err; int i, err;
printk(KERN_INFO printk(KERN_INFO
"ETRAX 100LX 10/100MBit ethernet v2.0 (c) 2000-2003 Axis Communications AB\n"); "ETRAX 100LX 10/100MBit ethernet v2.0 (c) 2000-2003 Axis Communications AB\n");
dev = alloc_etherdev(sizeof(struct net_local)); dev = alloc_etherdev(sizeof(struct net_local));
np = dev->priv;
if (!dev) if (!dev)
return -ENOMEM; return -ENOMEM;
...@@ -483,7 +498,6 @@ etrax_ethernet_init(void) ...@@ -483,7 +498,6 @@ etrax_ethernet_init(void)
dev->do_ioctl = e100_ioctl; dev->do_ioctl = e100_ioctl;
dev->set_config = e100_set_config; dev->set_config = e100_set_config;
dev->tx_timeout = e100_tx_timeout; dev->tx_timeout = e100_tx_timeout;
SET_ETHTOOL_OPS(dev, &ethtool_ops);
/* Initialise the list of Etrax DMA-descriptors */ /* Initialise the list of Etrax DMA-descriptors */
...@@ -546,6 +560,7 @@ etrax_ethernet_init(void) ...@@ -546,6 +560,7 @@ etrax_ethernet_init(void)
current_speed = 10; current_speed = 10;
current_speed_selection = 0; /* Auto */ current_speed_selection = 0; /* Auto */
speed_timer.expires = jiffies + NET_LINK_UP_CHECK_INTERVAL; speed_timer.expires = jiffies + NET_LINK_UP_CHECK_INTERVAL;
duplex_timer.data = (unsigned long)dev;
speed_timer.function = e100_check_speed; speed_timer.function = e100_check_speed;
clear_led_timer.function = e100_clear_network_leds; clear_led_timer.function = e100_clear_network_leds;
...@@ -553,8 +568,17 @@ etrax_ethernet_init(void) ...@@ -553,8 +568,17 @@ etrax_ethernet_init(void)
full_duplex = 0; full_duplex = 0;
current_duplex = autoneg; current_duplex = autoneg;
duplex_timer.expires = jiffies + NET_DUPLEX_CHECK_INTERVAL; duplex_timer.expires = jiffies + NET_DUPLEX_CHECK_INTERVAL;
duplex_timer.data = (unsigned long)dev;
duplex_timer.function = e100_check_duplex; duplex_timer.function = e100_check_duplex;
/* Initialize mii interface */
np->mii_if.phy_id = mdio_phy_addr;
np->mii_if.phy_id_mask = 0x1f;
np->mii_if.reg_num_mask = 0x1f;
np->mii_if.dev = dev;
np->mii_if.mdio_read = e100_get_mdio_reg;
np->mii_if.mdio_write = e100_set_mdio_reg;
/* Initialize group address registers to make sure that no */ /* Initialize group address registers to make sure that no */
/* unwanted addresses are matched */ /* unwanted addresses are matched */
*R_NETWORK_GA_0 = 0x00000000; *R_NETWORK_GA_0 = 0x00000000;
...@@ -645,8 +669,8 @@ e100_open(struct net_device *dev) ...@@ -645,8 +669,8 @@ e100_open(struct net_device *dev)
/* allocate the irq corresponding to the receiving DMA */ /* allocate the irq corresponding to the receiving DMA */
if (request_irq(NETWORK_DMA_RX_IRQ_NBR, e100rxtx_interrupt, 0, if (request_irq(NETWORK_DMA_RX_IRQ_NBR, e100rxtx_interrupt,
cardname, (void *)dev)) { SA_SAMPLE_RANDOM, cardname, (void *)dev)) {
goto grace_exit0; goto grace_exit0;
} }
...@@ -734,7 +758,7 @@ e100_open(struct net_device *dev) ...@@ -734,7 +758,7 @@ e100_open(struct net_device *dev)
restore_flags(flags); restore_flags(flags);
/* Probe for transceiver */ /* Probe for transceiver */
if (e100_probe_transceiver()) if (e100_probe_transceiver(dev))
goto grace_exit3; goto grace_exit3;
/* Start duplex/speed timers */ /* Start duplex/speed timers */
...@@ -760,45 +784,54 @@ e100_open(struct net_device *dev) ...@@ -760,45 +784,54 @@ e100_open(struct net_device *dev)
static void static void
generic_check_speed(void) generic_check_speed(struct net_device* dev)
{ {
unsigned long data; unsigned long data;
data = e100_get_mdio_reg(MDIO_ADVERTISMENT_REG); data = e100_get_mdio_reg(dev, mdio_phy_addr, MII_ADVERTISE);
if ((data & MDIO_ADVERT_100_FD) || if ((data & ADVERTISE_100FULL) ||
(data & MDIO_ADVERT_100_HD)) (data & ADVERTISE_100HALF))
current_speed = 100; current_speed = 100;
else else
current_speed = 10; current_speed = 10;
} }
static void static void
tdk_check_speed(void) tdk_check_speed(struct net_device* dev)
{ {
unsigned long data; unsigned long data;
data = e100_get_mdio_reg(MDIO_TDK_DIAGNOSTIC_REG); data = e100_get_mdio_reg(dev, mdio_phy_addr, MDIO_TDK_DIAGNOSTIC_REG);
current_speed = (data & MDIO_TDK_DIAGNOSTIC_RATE ? 100 : 10); current_speed = (data & MDIO_TDK_DIAGNOSTIC_RATE ? 100 : 10);
} }
static void static void
broadcom_check_speed(void) broadcom_check_speed(struct net_device* dev)
{ {
unsigned long data; unsigned long data;
data = e100_get_mdio_reg(MDIO_AUX_CTRL_STATUS_REG); data = e100_get_mdio_reg(dev, mdio_phy_addr, MDIO_AUX_CTRL_STATUS_REG);
current_speed = (data & MDIO_BC_SPEED ? 100 : 10); current_speed = (data & MDIO_BC_SPEED ? 100 : 10);
} }
static void static void
e100_check_speed(unsigned long dummy) intel_check_speed(struct net_device* dev)
{
unsigned long data;
data = e100_get_mdio_reg(dev, mdio_phy_addr, MDIO_INT_STATUS_REG_2);
current_speed = (data & MDIO_INT_SPEED ? 100 : 10);
}
static void
e100_check_speed(unsigned long priv)
{ {
struct net_device* dev = (struct net_device*)priv;
static int led_initiated = 0; static int led_initiated = 0;
unsigned long data; unsigned long data;
int old_speed = current_speed; int old_speed = current_speed;
data = e100_get_mdio_reg(MDIO_BASE_STATUS_REG); data = e100_get_mdio_reg(dev, mdio_phy_addr, MII_BMSR);
if (!(data & MDIO_LINK_UP_MASK)) { if (!(data & BMSR_LSTATUS)) {
current_speed = 0; current_speed = 0;
} else { } else {
transceiver->check_speed(); transceiver->check_speed(dev);
} }
if ((old_speed != current_speed) || !led_initiated) { if ((old_speed != current_speed) || !led_initiated) {
...@@ -812,70 +845,73 @@ e100_check_speed(unsigned long dummy) ...@@ -812,70 +845,73 @@ e100_check_speed(unsigned long dummy)
} }
static void static void
e100_negotiate(void) e100_negotiate(struct net_device* dev)
{ {
unsigned short data = e100_get_mdio_reg(MDIO_ADVERTISMENT_REG); unsigned short data = e100_get_mdio_reg(dev, mdio_phy_addr, MII_ADVERTISE);
/* Discard old speed and duplex settings */ /* Discard old speed and duplex settings */
data &= ~(MDIO_ADVERT_100_HD | MDIO_ADVERT_100_FD | data &= ~(ADVERTISE_100HALF | ADVERTISE_100FULL |
MDIO_ADVERT_10_FD | MDIO_ADVERT_10_HD); ADVERTISE_10HALF | ADVERTISE_10FULL);
switch (current_speed_selection) { switch (current_speed_selection) {
case 10 : case 10 :
if (current_duplex == full) if (current_duplex == full)
data |= MDIO_ADVERT_10_FD; data |= ADVERTISE_10FULL;
else if (current_duplex == half) else if (current_duplex == half)
data |= MDIO_ADVERT_10_HD; data |= ADVERTISE_10HALF;
else else
data |= MDIO_ADVERT_10_HD | MDIO_ADVERT_10_FD; data |= ADVERTISE_10HALF | ADVERTISE_10FULL;
break; break;
case 100 : case 100 :
if (current_duplex == full) if (current_duplex == full)
data |= MDIO_ADVERT_100_FD; data |= ADVERTISE_100FULL;
else if (current_duplex == half) else if (current_duplex == half)
data |= MDIO_ADVERT_100_HD; data |= ADVERTISE_100HALF;
else else
data |= MDIO_ADVERT_100_HD | MDIO_ADVERT_100_FD; data |= ADVERTISE_100HALF | ADVERTISE_100FULL;
break; break;
case 0 : /* Auto */ case 0 : /* Auto */
if (current_duplex == full) if (current_duplex == full)
data |= MDIO_ADVERT_100_FD | MDIO_ADVERT_10_FD; data |= ADVERTISE_100FULL | ADVERTISE_10FULL;
else if (current_duplex == half) else if (current_duplex == half)
data |= MDIO_ADVERT_100_HD | MDIO_ADVERT_10_HD; data |= ADVERTISE_100HALF | ADVERTISE_10HALF;
else else
data |= MDIO_ADVERT_100_HD | MDIO_ADVERT_100_FD | MDIO_ADVERT_10_FD | MDIO_ADVERT_10_HD; data |= ADVERTISE_10HALF | ADVERTISE_10FULL |
ADVERTISE_100HALF | ADVERTISE_100FULL;
break; break;
default : /* assume autoneg speed and duplex */ default : /* assume autoneg speed and duplex */
data |= MDIO_ADVERT_100_HD | MDIO_ADVERT_100_FD | data |= ADVERTISE_10HALF | ADVERTISE_10FULL |
MDIO_ADVERT_10_FD | MDIO_ADVERT_10_HD; ADVERTISE_100HALF | ADVERTISE_100FULL;
} }
e100_set_mdio_reg(MDIO_ADVERTISMENT_REG, data); e100_set_mdio_reg(dev, mdio_phy_addr, MII_ADVERTISE, data);
/* Renegotiate with link partner */ /* Renegotiate with link partner */
data = e100_get_mdio_reg(MDIO_BASE_CONTROL_REG); data = e100_get_mdio_reg(dev, mdio_phy_addr, MII_BMCR);
data |= MDIO_BC_NEGOTIATE; data |= BMCR_ANENABLE | BMCR_ANRESTART;
e100_set_mdio_reg(MDIO_BASE_CONTROL_REG, data); e100_set_mdio_reg(dev, mdio_phy_addr, MII_BMCR, data);
} }
static void static void
e100_set_speed(unsigned long speed) e100_set_speed(struct net_device* dev, unsigned long speed)
{ {
if (speed != current_speed_selection) { if (speed != current_speed_selection) {
current_speed_selection = speed; current_speed_selection = speed;
e100_negotiate(); e100_negotiate(dev);
} }
} }
static void static void
e100_check_duplex(unsigned long dummy) e100_check_duplex(unsigned long priv)
{ {
struct net_device *dev = (struct net_device *)priv;
struct net_local *np = (struct net_local *)dev->priv;
int old_duplex = full_duplex; int old_duplex = full_duplex;
transceiver->check_duplex(); transceiver->check_duplex(dev);
if (old_duplex != full_duplex) { if (old_duplex != full_duplex) {
/* Duplex changed */ /* Duplex changed */
SETF(network_rec_config_shadow, R_NETWORK_REC_CONFIG, duplex, full_duplex); SETF(network_rec_config_shadow, R_NETWORK_REC_CONFIG, duplex, full_duplex);
...@@ -885,47 +921,56 @@ e100_check_duplex(unsigned long dummy) ...@@ -885,47 +921,56 @@ e100_check_duplex(unsigned long dummy)
/* Reinitialize the timer. */ /* Reinitialize the timer. */
duplex_timer.expires = jiffies + NET_DUPLEX_CHECK_INTERVAL; duplex_timer.expires = jiffies + NET_DUPLEX_CHECK_INTERVAL;
add_timer(&duplex_timer); add_timer(&duplex_timer);
np->mii_if.full_duplex = full_duplex;
} }
static void static void
generic_check_duplex(void) generic_check_duplex(struct net_device* dev)
{ {
unsigned long data; unsigned long data;
data = e100_get_mdio_reg(MDIO_ADVERTISMENT_REG); data = e100_get_mdio_reg(dev, mdio_phy_addr, MII_ADVERTISE);
if ((data & MDIO_ADVERT_100_FD) || if ((data & ADVERTISE_10FULL) ||
(data & MDIO_ADVERT_10_FD)) (data & ADVERTISE_100FULL))
full_duplex = 1; full_duplex = 1;
else else
full_duplex = 0; full_duplex = 0;
} }
static void static void
tdk_check_duplex(void) tdk_check_duplex(struct net_device* dev)
{ {
unsigned long data; unsigned long data;
data = e100_get_mdio_reg(MDIO_TDK_DIAGNOSTIC_REG); data = e100_get_mdio_reg(dev, mdio_phy_addr, MDIO_TDK_DIAGNOSTIC_REG);
full_duplex = (data & MDIO_TDK_DIAGNOSTIC_DPLX) ? 1 : 0; full_duplex = (data & MDIO_TDK_DIAGNOSTIC_DPLX) ? 1 : 0;
} }
static void static void
broadcom_check_duplex(void) broadcom_check_duplex(struct net_device* dev)
{ {
unsigned long data; unsigned long data;
data = e100_get_mdio_reg(MDIO_AUX_CTRL_STATUS_REG); data = e100_get_mdio_reg(dev, mdio_phy_addr, MDIO_AUX_CTRL_STATUS_REG);
full_duplex = (data & MDIO_BC_FULL_DUPLEX_IND) ? 1 : 0; full_duplex = (data & MDIO_BC_FULL_DUPLEX_IND) ? 1 : 0;
} }
static void
intel_check_duplex(struct net_device* dev)
{
unsigned long data;
data = e100_get_mdio_reg(dev, mdio_phy_addr, MDIO_INT_STATUS_REG_2);
full_duplex = (data & MDIO_INT_FULL_DUPLEX_IND) ? 1 : 0;
}
static void static void
e100_set_duplex(enum duplex new_duplex) e100_set_duplex(struct net_device* dev, enum duplex new_duplex)
{ {
if (new_duplex != current_duplex) { if (new_duplex != current_duplex) {
current_duplex = new_duplex; current_duplex = new_duplex;
e100_negotiate(); e100_negotiate(dev);
} }
} }
static int static int
e100_probe_transceiver(void) e100_probe_transceiver(struct net_device* dev)
{ {
unsigned int phyid_high; unsigned int phyid_high;
unsigned int phyid_low; unsigned int phyid_low;
...@@ -934,15 +979,15 @@ e100_probe_transceiver(void) ...@@ -934,15 +979,15 @@ e100_probe_transceiver(void)
/* Probe MDIO physical address */ /* Probe MDIO physical address */
for (mdio_phy_addr = 0; mdio_phy_addr <= 31; mdio_phy_addr++) { for (mdio_phy_addr = 0; mdio_phy_addr <= 31; mdio_phy_addr++) {
if (e100_get_mdio_reg(MDIO_BASE_STATUS_REG) != 0xffff) if (e100_get_mdio_reg(dev, mdio_phy_addr, MII_BMSR) != 0xffff)
break; break;
} }
if (mdio_phy_addr == 32) if (mdio_phy_addr == 32)
return -ENODEV; return -ENODEV;
/* Get manufacturer */ /* Get manufacturer */
phyid_high = e100_get_mdio_reg(MDIO_PHY_ID_HIGH_REG); phyid_high = e100_get_mdio_reg(dev, mdio_phy_addr, MII_PHYSID1);
phyid_low = e100_get_mdio_reg(MDIO_PHY_ID_LOW_REG); phyid_low = e100_get_mdio_reg(dev, mdio_phy_addr, MII_PHYSID2);
oui = (phyid_high << 6) | (phyid_low >> 10); oui = (phyid_high << 6) | (phyid_low >> 10);
for (ops = &transceivers[0]; ops->oui; ops++) { for (ops = &transceivers[0]; ops->oui; ops++) {
...@@ -954,16 +999,16 @@ e100_probe_transceiver(void) ...@@ -954,16 +999,16 @@ e100_probe_transceiver(void)
return 0; return 0;
} }
static unsigned short static int
e100_get_mdio_reg(unsigned char reg_num) e100_get_mdio_reg(struct net_device *dev, int phy_id, int location)
{ {
unsigned short cmd; /* Data to be sent on MDIO port */ unsigned short cmd; /* Data to be sent on MDIO port */
unsigned short data; /* Data read from MDIO */ int data; /* Data read from MDIO */
int bitCounter; int bitCounter;
/* Start of frame, OP Code, Physical Address, Register Address */ /* Start of frame, OP Code, Physical Address, Register Address */
cmd = (MDIO_START << 14) | (MDIO_READ << 12) | (mdio_phy_addr << 7) | cmd = (MDIO_START << 14) | (MDIO_READ << 12) | (phy_id << 7) |
(reg_num << 2); (location << 2);
e100_send_mdio_cmd(cmd, 0); e100_send_mdio_cmd(cmd, 0);
...@@ -978,19 +1023,19 @@ e100_get_mdio_reg(unsigned char reg_num) ...@@ -978,19 +1023,19 @@ e100_get_mdio_reg(unsigned char reg_num)
} }
static void static void
e100_set_mdio_reg(unsigned char reg, unsigned short data) e100_set_mdio_reg(struct net_device *dev, int phy_id, int location, int value)
{ {
int bitCounter; int bitCounter;
unsigned short cmd; unsigned short cmd;
cmd = (MDIO_START << 14) | (MDIO_WRITE << 12) | (mdio_phy_addr << 7) | cmd = (MDIO_START << 14) | (MDIO_WRITE << 12) | (phy_id << 7) |
(reg << 2); (location << 2);
e100_send_mdio_cmd(cmd, 1); e100_send_mdio_cmd(cmd, 1);
/* Data... */ /* Data... */
for (bitCounter=15; bitCounter>=0 ; bitCounter--) { for (bitCounter=15; bitCounter>=0 ; bitCounter--) {
e100_send_mdio_bit(GET_BIT(bitCounter, data)); e100_send_mdio_bit(GET_BIT(bitCounter, value));
} }
} }
...@@ -1043,15 +1088,15 @@ e100_receive_mdio_bit() ...@@ -1043,15 +1088,15 @@ e100_receive_mdio_bit()
} }
static void static void
e100_reset_transceiver(void) e100_reset_transceiver(struct net_device* dev)
{ {
unsigned short cmd; unsigned short cmd;
unsigned short data; unsigned short data;
int bitCounter; int bitCounter;
data = e100_get_mdio_reg(MDIO_BASE_CONTROL_REG); data = e100_get_mdio_reg(dev, mdio_phy_addr, MII_BMCR);
cmd = (MDIO_START << 14) | (MDIO_WRITE << 12) | (mdio_phy_addr << 7) | (MDIO_BASE_CONTROL_REG << 2); cmd = (MDIO_START << 14) | (MDIO_WRITE << 12) | (mdio_phy_addr << 7) | (MII_BMCR << 2);
e100_send_mdio_cmd(cmd, 1); e100_send_mdio_cmd(cmd, 1);
...@@ -1088,7 +1133,7 @@ e100_tx_timeout(struct net_device *dev) ...@@ -1088,7 +1133,7 @@ e100_tx_timeout(struct net_device *dev)
/* Reset the transceiver. */ /* Reset the transceiver. */
e100_reset_transceiver(); e100_reset_transceiver(dev);
/* and get rid of the packets that never got an interrupt */ /* and get rid of the packets that never got an interrupt */
while (myFirstTxDesc != myNextTxDesc) while (myFirstTxDesc != myNextTxDesc)
...@@ -1170,7 +1215,8 @@ e100rxtx_interrupt(int irq, void *dev_id, struct pt_regs * regs) ...@@ -1170,7 +1215,8 @@ e100rxtx_interrupt(int irq, void *dev_id, struct pt_regs * regs)
/* check if one or more complete packets were indeed received */ /* check if one or more complete packets were indeed received */
while (*R_DMA_CH1_FIRST != virt_to_phys(myNextRxDesc)) { while ((*R_DMA_CH1_FIRST != virt_to_phys(myNextRxDesc)) &&
(myNextRxDesc != myLastRxDesc)) {
/* Take out the buffer and give it to the OS, then /* Take out the buffer and give it to the OS, then
* allocate a new buffer to put a packet in. * allocate a new buffer to put a packet in.
*/ */
...@@ -1402,34 +1448,36 @@ e100_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) ...@@ -1402,34 +1448,36 @@ e100_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
spin_lock(&np->lock); /* Preempt protection */ spin_lock(&np->lock); /* Preempt protection */
switch (cmd) { switch (cmd) {
case SIOCETHTOOL:
return e100_ethtool_ioctl(dev,ifr);
case SIOCGMIIPHY: /* Get PHY address */ case SIOCGMIIPHY: /* Get PHY address */
data->phy_id = mdio_phy_addr; data->phy_id = mdio_phy_addr;
break; break;
case SIOCGMIIREG: /* Read MII register */ case SIOCGMIIREG: /* Read MII register */
data->val_out = e100_get_mdio_reg(data->reg_num); data->val_out = e100_get_mdio_reg(dev, mdio_phy_addr, data->reg_num);
break; break;
case SIOCSMIIREG: /* Write MII register */ case SIOCSMIIREG: /* Write MII register */
e100_set_mdio_reg(data->reg_num, data->val_in); e100_set_mdio_reg(dev, mdio_phy_addr, data->reg_num, data->val_in);
break; break;
/* The ioctls below should be considered obsolete but are */ /* The ioctls below should be considered obsolete but are */
/* still present for compatability with old scripts/apps */ /* still present for compatability with old scripts/apps */
case SET_ETH_SPEED_10: /* 10 Mbps */ case SET_ETH_SPEED_10: /* 10 Mbps */
e100_set_speed(10); e100_set_speed(dev, 10);
break; break;
case SET_ETH_SPEED_100: /* 100 Mbps */ case SET_ETH_SPEED_100: /* 100 Mbps */
e100_set_speed(100); e100_set_speed(dev, 100);
break; break;
case SET_ETH_SPEED_AUTO: /* Auto negotiate speed */ case SET_ETH_SPEED_AUTO: /* Auto negotiate speed */
e100_set_speed(0); e100_set_speed(dev, 0);
break; break;
case SET_ETH_DUPLEX_HALF: /* Half duplex. */ case SET_ETH_DUPLEX_HALF: /* Half duplex. */
e100_set_duplex(half); e100_set_duplex(dev, half);
break; break;
case SET_ETH_DUPLEX_FULL: /* Full duplex. */ case SET_ETH_DUPLEX_FULL: /* Full duplex. */
e100_set_duplex(full); e100_set_duplex(dev, full);
break; break;
case SET_ETH_DUPLEX_AUTO: /* Autonegotiate duplex*/ case SET_ETH_DUPLEX_AUTO: /* Autonegotiate duplex*/
e100_set_duplex(autoneg); e100_set_duplex(dev, autoneg);
break; break;
default: default:
return -EINVAL; return -EINVAL;
...@@ -1438,71 +1486,88 @@ e100_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) ...@@ -1438,71 +1486,88 @@ e100_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
return 0; return 0;
} }
static int e100_get_settings(struct net_device *dev, struct ethtool_cmd *ecmd) static int
{ e100_ethtool_ioctl(struct net_device *dev, struct ifreq *ifr)
ecmd->supported =
SUPPORTED_Autoneg | SUPPORTED_TP | SUPPORTED_MII |
SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full |
SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full;
ecmd->port = PORT_TP;
ecmd->transceiver = XCVR_EXTERNAL;
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;
else {
ecmd->advertising |=
ADVERTISED_10baseT_Half | ADVERTISED_10baseT_Full |
ADVERTISED_100baseT_Half | ADVERTISED_100baseT_Full;
if (current_speed_selection == 10)
ecmd->advertising &= ~(ADVERTISED_100baseT_Half | ADVERTISED_100baseT_Full);
else if (current_speed_selection == 100)
ecmd->advertising &= ~(ADVERTISED_10baseT_Half | ADVERTISED_10baseT_Full);
if (current_duplex == half)
ecmd->advertising &= ~(ADVERTISED_10baseT_Full | ADVERTISED_100baseT_Full);
else if (current_duplex == full)
ecmd->advertising &= ~(ADVERTISED_10baseT_Half | ADVERTISED_100baseT_Half);
}
ecmd->autoneg = AUTONEG_ENABLE;
return 0;
}
static int e100_set_settings(struct net_device *dev, struct ethtool_cmd *ecmd)
{ {
if (ecmd->autoneg == AUTONEG_ENABLE) { struct ethtool_cmd ecmd;
e100_set_duplex(autoneg);
e100_set_speed(0); if (copy_from_user(&ecmd, ifr->ifr_data, sizeof (ecmd)))
} else { return -EFAULT;
e100_set_duplex(ecmd->duplex == DUPLEX_HALF ? half : full);
e100_set_speed(ecmd->speed == SPEED_10 ? 10: 100); switch (ecmd.cmd) {
case ETHTOOL_GSET:
{
memset((void *) &ecmd, 0, sizeof (ecmd));
ecmd.supported =
SUPPORTED_Autoneg | SUPPORTED_TP | SUPPORTED_MII |
SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full |
SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full;
ecmd.port = PORT_TP;
ecmd.transceiver = XCVR_EXTERNAL;
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;
else {
ecmd.advertising |=
ADVERTISED_10baseT_Half | ADVERTISED_10baseT_Full |
ADVERTISED_100baseT_Half | ADVERTISED_100baseT_Full;
if (current_speed_selection == 10)
ecmd.advertising &= ~(ADVERTISED_100baseT_Half | ADVERTISED_100baseT_Full);
else if (current_speed_selection == 100)
ecmd.advertising &= ~(ADVERTISED_10baseT_Half | ADVERTISED_10baseT_Full);
if (current_duplex == half)
ecmd.advertising &= ~(ADVERTISED_10baseT_Full | ADVERTISED_100baseT_Full);
else if (current_duplex == full)
ecmd.advertising &= ~(ADVERTISED_10baseT_Half | ADVERTISED_100baseT_Half);
}
ecmd.autoneg = AUTONEG_ENABLE;
if (copy_to_user(ifr->ifr_data, &ecmd, sizeof (ecmd)))
return -EFAULT;
}
break;
case ETHTOOL_SSET:
{
if (!capable(CAP_NET_ADMIN)) {
return -EPERM;
}
if (ecmd.autoneg == AUTONEG_ENABLE) {
e100_set_duplex(dev, autoneg);
e100_set_speed(dev, 0);
} else {
e100_set_duplex(dev, ecmd.duplex == DUPLEX_HALF ? half : full);
e100_set_speed(dev, ecmd.speed == SPEED_10 ? 10: 100);
}
}
break;
case ETHTOOL_GDRVINFO:
{
struct ethtool_drvinfo info;
memset((void *) &info, 0, sizeof (info));
strncpy(info.driver, "ETRAX 100LX", sizeof(info.driver) - 1);
strncpy(info.version, "$Revision: 1.31 $", 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;
info.eedump_len = 0;
info.testinfo_len = 0;
if (copy_to_user(ifr->ifr_data, &info, sizeof (info)))
return -EFAULT;
}
break;
case ETHTOOL_NWAY_RST:
if (current_duplex == autoneg && current_speed_selection == 0)
e100_negotiate(dev);
break;
default:
return -EOPNOTSUPP;
break;
} }
return 0; return 0;
} }
static void e100_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
{
strncpy(info->driver, "ETRAX 100LX", sizeof(info->driver) - 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);
}
static int e100_nway_reset(struct net_device *dev)
{
if (current_duplex == autoneg && current_speed_selection == 0)
e100_negotiate();
return 0;
}
static struct ethtool_ops ethtool_ops = {
.get_settings = e100_get_settings,
.set_settings = e100_set_settings,
.get_drvinfo = e100_get_drvinfo,
.nway_reset = e100_nway_reset,
};
static int static int
e100_set_config(struct net_device *dev, struct ifmap *map) e100_set_config(struct net_device *dev, struct ifmap *map)
{ {
...@@ -1512,17 +1577,17 @@ e100_set_config(struct net_device *dev, struct ifmap *map) ...@@ -1512,17 +1577,17 @@ e100_set_config(struct net_device *dev, struct ifmap *map)
switch(map->port) { switch(map->port) {
case IF_PORT_UNKNOWN: case IF_PORT_UNKNOWN:
/* Use autoneg */ /* Use autoneg */
e100_set_speed(0); e100_set_speed(dev, 0);
e100_set_duplex(autoneg); e100_set_duplex(dev, autoneg);
break; break;
case IF_PORT_10BASET: case IF_PORT_10BASET:
e100_set_speed(10); e100_set_speed(dev, 10);
e100_set_duplex(autoneg); e100_set_duplex(dev, autoneg);
break; break;
case IF_PORT_100BASET: case IF_PORT_100BASET:
case IF_PORT_100BASETX: case IF_PORT_100BASETX:
e100_set_speed(100); e100_set_speed(dev, 100);
e100_set_duplex(autoneg); e100_set_duplex(dev, autoneg);
break; break;
case IF_PORT_100BASEFX: case IF_PORT_100BASEFX:
case IF_PORT_10BASE2: case IF_PORT_10BASE2:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment