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.
*
......@@ -7,6 +7,27 @@
* The outline of this driver comes from skeleton.c.
*
* $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
* Merge of changes from 2.4
*
......@@ -222,13 +243,13 @@
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include <linux/ethtool.h>
#include <linux/bitops.h>
#include <asm/arch/svinto.h>/* DMA and register descriptions */
#include <asm/io.h> /* LED_* I/O functions */
#include <asm/irq.h>
#include <asm/dma.h>
#include <asm/system.h>
#include <asm/bitops.h>
#include <asm/ethernet.h>
#include <asm/cache.h>
......@@ -252,6 +273,7 @@ static struct sockaddr default_mac = {
/* Information that need to be kept for each board. */
struct net_local {
struct net_device_stats stats;
struct mii_if_info mii_if;
/* Tx control lock. This protects the transmit buffer ring
* state along with the "tx full" state of the driver. This
......@@ -271,8 +293,8 @@ typedef struct etrax_eth_descr
struct transceiver_ops
{
unsigned int oui;
void (*check_speed)(void);
void (*check_duplex)(void);
void (*check_speed)(struct net_device* dev);
void (*check_duplex)(struct net_device* dev);
};
struct transceiver_ops* transceiver;
......@@ -295,25 +317,6 @@ enum duplex
/*
** 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_READ 0x2
#define MDIO_WRITE 0x1
......@@ -329,6 +332,11 @@ enum duplex
#define MDIO_TDK_DIAGNOSTIC_RATE 0x400
#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 */
#define NET_FLASH_TIME (HZ/50) /* 20 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)
static void e100_rx(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_ethtool_ioctl(struct net_device* dev, struct ifreq *ifr);
static int e100_set_config(struct net_device* dev, struct ifmap* map);
static void e100_tx_timeout(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);
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 struct ethtool_ops ethtool_ops;
static int e100_probe_transceiver(struct net_device* dev);
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 void e100_set_speed(unsigned long speed);
static void e100_check_duplex(unsigned long dummy);
static void e100_set_duplex(enum duplex);
static void e100_negotiate(void);
static int e100_get_mdio_reg(struct net_device *dev, int phy_id, int location);
static void e100_set_mdio_reg(struct net_device *dev, int phy_id, int location, int value);
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_bit(unsigned char bit);
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_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);
static void broadcom_check_speed(struct net_device* dev);
static void broadcom_check_duplex(struct net_device* dev);
static void tdk_check_speed(struct net_device* dev);
static void tdk_check_duplex(struct net_device* dev);
static void intel_check_speed(struct net_device* dev);
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[] =
{
{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 */
{0x04de, intel_check_speed, intel_check_duplex}, /* Intel LXT972A*/
{0x0000, generic_check_speed, generic_check_duplex} /* Generic, must be last */
};
......@@ -456,12 +468,15 @@ static int __init
etrax_ethernet_init(void)
{
struct net_device *dev;
struct net_local* np;
int i, err;
printk(KERN_INFO
"ETRAX 100LX 10/100MBit ethernet v2.0 (c) 2000-2003 Axis Communications AB\n");
dev = alloc_etherdev(sizeof(struct net_local));
np = dev->priv;
if (!dev)
return -ENOMEM;
......@@ -483,7 +498,6 @@ etrax_ethernet_init(void)
dev->do_ioctl = e100_ioctl;
dev->set_config = e100_set_config;
dev->tx_timeout = e100_tx_timeout;
SET_ETHTOOL_OPS(dev, &ethtool_ops);
/* Initialise the list of Etrax DMA-descriptors */
......@@ -546,6 +560,7 @@ etrax_ethernet_init(void)
current_speed = 10;
current_speed_selection = 0; /* Auto */
speed_timer.expires = jiffies + NET_LINK_UP_CHECK_INTERVAL;
duplex_timer.data = (unsigned long)dev;
speed_timer.function = e100_check_speed;
clear_led_timer.function = e100_clear_network_leds;
......@@ -553,8 +568,17 @@ etrax_ethernet_init(void)
full_duplex = 0;
current_duplex = autoneg;
duplex_timer.expires = jiffies + NET_DUPLEX_CHECK_INTERVAL;
duplex_timer.data = (unsigned long)dev;
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 */
/* unwanted addresses are matched */
*R_NETWORK_GA_0 = 0x00000000;
......@@ -645,8 +669,8 @@ e100_open(struct net_device *dev)
/* allocate the irq corresponding to the receiving DMA */
if (request_irq(NETWORK_DMA_RX_IRQ_NBR, e100rxtx_interrupt, 0,
cardname, (void *)dev)) {
if (request_irq(NETWORK_DMA_RX_IRQ_NBR, e100rxtx_interrupt,
SA_SAMPLE_RANDOM, cardname, (void *)dev)) {
goto grace_exit0;
}
......@@ -734,7 +758,7 @@ e100_open(struct net_device *dev)
restore_flags(flags);
/* Probe for transceiver */
if (e100_probe_transceiver())
if (e100_probe_transceiver(dev))
goto grace_exit3;
/* Start duplex/speed timers */
......@@ -760,45 +784,54 @@ e100_open(struct net_device *dev)
static void
generic_check_speed(void)
generic_check_speed(struct net_device* dev)
{
unsigned long data;
data = e100_get_mdio_reg(MDIO_ADVERTISMENT_REG);
if ((data & MDIO_ADVERT_100_FD) ||
(data & MDIO_ADVERT_100_HD))
data = e100_get_mdio_reg(dev, mdio_phy_addr, MII_ADVERTISE);
if ((data & ADVERTISE_100FULL) ||
(data & ADVERTISE_100HALF))
current_speed = 100;
else
current_speed = 10;
}
static void
tdk_check_speed(void)
tdk_check_speed(struct net_device* dev)
{
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);
}
static void
broadcom_check_speed(void)
broadcom_check_speed(struct net_device* dev)
{
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);
}
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;
unsigned long data;
int old_speed = current_speed;
data = e100_get_mdio_reg(MDIO_BASE_STATUS_REG);
if (!(data & MDIO_LINK_UP_MASK)) {
data = e100_get_mdio_reg(dev, mdio_phy_addr, MII_BMSR);
if (!(data & BMSR_LSTATUS)) {
current_speed = 0;
} else {
transceiver->check_speed();
transceiver->check_speed(dev);
}
if ((old_speed != current_speed) || !led_initiated) {
......@@ -812,70 +845,73 @@ e100_check_speed(unsigned long dummy)
}
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 */
data &= ~(MDIO_ADVERT_100_HD | MDIO_ADVERT_100_FD |
MDIO_ADVERT_10_FD | MDIO_ADVERT_10_HD);
data &= ~(ADVERTISE_100HALF | ADVERTISE_100FULL |
ADVERTISE_10HALF | ADVERTISE_10FULL);
switch (current_speed_selection) {
case 10 :
if (current_duplex == full)
data |= MDIO_ADVERT_10_FD;
data |= ADVERTISE_10FULL;
else if (current_duplex == half)
data |= MDIO_ADVERT_10_HD;
data |= ADVERTISE_10HALF;
else
data |= MDIO_ADVERT_10_HD | MDIO_ADVERT_10_FD;
data |= ADVERTISE_10HALF | ADVERTISE_10FULL;
break;
case 100 :
if (current_duplex == full)
data |= MDIO_ADVERT_100_FD;
data |= ADVERTISE_100FULL;
else if (current_duplex == half)
data |= MDIO_ADVERT_100_HD;
data |= ADVERTISE_100HALF;
else
data |= MDIO_ADVERT_100_HD | MDIO_ADVERT_100_FD;
data |= ADVERTISE_100HALF | ADVERTISE_100FULL;
break;
case 0 : /* Auto */
if (current_duplex == full)
data |= MDIO_ADVERT_100_FD | MDIO_ADVERT_10_FD;
data |= ADVERTISE_100FULL | ADVERTISE_10FULL;
else if (current_duplex == half)
data |= MDIO_ADVERT_100_HD | MDIO_ADVERT_10_HD;
data |= ADVERTISE_100HALF | ADVERTISE_10HALF;
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;
default : /* assume autoneg speed and duplex */
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;
}
e100_set_mdio_reg(MDIO_ADVERTISMENT_REG, data);
e100_set_mdio_reg(dev, mdio_phy_addr, MII_ADVERTISE, data);
/* Renegotiate with link partner */
data = e100_get_mdio_reg(MDIO_BASE_CONTROL_REG);
data |= MDIO_BC_NEGOTIATE;
data = e100_get_mdio_reg(dev, mdio_phy_addr, MII_BMCR);
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
e100_set_speed(unsigned long speed)
e100_set_speed(struct net_device* dev, unsigned long speed)
{
if (speed != current_speed_selection) {
current_speed_selection = speed;
e100_negotiate();
e100_negotiate(dev);
}
}
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;
transceiver->check_duplex();
transceiver->check_duplex(dev);
if (old_duplex != full_duplex) {
/* Duplex changed */
SETF(network_rec_config_shadow, R_NETWORK_REC_CONFIG, duplex, full_duplex);
......@@ -885,47 +921,56 @@ e100_check_duplex(unsigned long dummy)
/* Reinitialize the timer. */
duplex_timer.expires = jiffies + NET_DUPLEX_CHECK_INTERVAL;
add_timer(&duplex_timer);
np->mii_if.full_duplex = full_duplex;
}
static void
generic_check_duplex(void)
generic_check_duplex(struct net_device* dev)
{
unsigned long data;
data = e100_get_mdio_reg(MDIO_ADVERTISMENT_REG);
if ((data & MDIO_ADVERT_100_FD) ||
(data & MDIO_ADVERT_10_FD))
data = e100_get_mdio_reg(dev, mdio_phy_addr, MII_ADVERTISE);
if ((data & ADVERTISE_10FULL) ||
(data & ADVERTISE_100FULL))
full_duplex = 1;
else
full_duplex = 0;
}
static void
tdk_check_duplex(void)
tdk_check_duplex(struct net_device* dev)
{
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;
}
static void
broadcom_check_duplex(void)
broadcom_check_duplex(struct net_device* dev)
{
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;
}
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
e100_set_duplex(enum duplex new_duplex)
e100_set_duplex(struct net_device* dev, enum duplex new_duplex)
{
if (new_duplex != current_duplex) {
current_duplex = new_duplex;
e100_negotiate();
e100_negotiate(dev);
}
}
static int
e100_probe_transceiver(void)
e100_probe_transceiver(struct net_device* dev)
{
unsigned int phyid_high;
unsigned int phyid_low;
......@@ -934,15 +979,15 @@ e100_probe_transceiver(void)
/* 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)
if (e100_get_mdio_reg(dev, mdio_phy_addr, MII_BMSR) != 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);
phyid_high = e100_get_mdio_reg(dev, mdio_phy_addr, MII_PHYSID1);
phyid_low = e100_get_mdio_reg(dev, mdio_phy_addr, MII_PHYSID2);
oui = (phyid_high << 6) | (phyid_low >> 10);
for (ops = &transceivers[0]; ops->oui; ops++) {
......@@ -954,16 +999,16 @@ e100_probe_transceiver(void)
return 0;
}
static unsigned short
e100_get_mdio_reg(unsigned char reg_num)
static int
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 data; /* Data read from MDIO */
int data; /* Data read from MDIO */
int bitCounter;
/* Start of frame, OP Code, Physical Address, Register Address */
cmd = (MDIO_START << 14) | (MDIO_READ << 12) | (mdio_phy_addr << 7) |
(reg_num << 2);
cmd = (MDIO_START << 14) | (MDIO_READ << 12) | (phy_id << 7) |
(location << 2);
e100_send_mdio_cmd(cmd, 0);
......@@ -978,19 +1023,19 @@ e100_get_mdio_reg(unsigned char reg_num)
}
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;
unsigned short cmd;
cmd = (MDIO_START << 14) | (MDIO_WRITE << 12) | (mdio_phy_addr << 7) |
(reg << 2);
cmd = (MDIO_START << 14) | (MDIO_WRITE << 12) | (phy_id << 7) |
(location << 2);
e100_send_mdio_cmd(cmd, 1);
/* Data... */
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()
}
static void
e100_reset_transceiver(void)
e100_reset_transceiver(struct net_device* dev)
{
unsigned short cmd;
unsigned short data;
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);
......@@ -1088,7 +1133,7 @@ e100_tx_timeout(struct net_device *dev)
/* Reset the transceiver. */
e100_reset_transceiver();
e100_reset_transceiver(dev);
/* and get rid of the packets that never got an interrupt */
while (myFirstTxDesc != myNextTxDesc)
......@@ -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 */
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
* allocate a new buffer to put a packet in.
*/
......@@ -1402,34 +1448,36 @@ e100_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
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_phy_addr;
break;
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;
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;
/* The ioctls below should be considered obsolete but are */
/* still present for compatability with old scripts/apps */
case SET_ETH_SPEED_10: /* 10 Mbps */
e100_set_speed(10);
e100_set_speed(dev, 10);
break;
case SET_ETH_SPEED_100: /* 100 Mbps */
e100_set_speed(100);
e100_set_speed(dev, 100);
break;
case SET_ETH_SPEED_AUTO: /* Auto negotiate speed */
e100_set_speed(0);
e100_set_speed(dev, 0);
break;
case SET_ETH_DUPLEX_HALF: /* Half duplex. */
e100_set_duplex(half);
e100_set_duplex(dev, half);
break;
case SET_ETH_DUPLEX_FULL: /* Full duplex. */
e100_set_duplex(full);
e100_set_duplex(dev, full);
break;
case SET_ETH_DUPLEX_AUTO: /* Autonegotiate duplex*/
e100_set_duplex(autoneg);
e100_set_duplex(dev, autoneg);
break;
default:
return -EINVAL;
......@@ -1438,71 +1486,88 @@ e100_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
return 0;
}
static int e100_get_settings(struct net_device *dev, struct ethtool_cmd *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;
return 0;
}
static int e100_set_settings(struct net_device *dev, struct ethtool_cmd *ecmd)
static int
e100_ethtool_ioctl(struct net_device *dev, struct ifreq *ifr)
{
if (ecmd->autoneg == AUTONEG_ENABLE) {
e100_set_duplex(autoneg);
e100_set_speed(0);
} else {
e100_set_duplex(ecmd->duplex == DUPLEX_HALF ? half : full);
e100_set_speed(ecmd->speed == SPEED_10 ? 10: 100);
struct ethtool_cmd ecmd;
if (copy_from_user(&ecmd, ifr->ifr_data, sizeof (ecmd)))
return -EFAULT;
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;
}
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
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) {
case IF_PORT_UNKNOWN:
/* Use autoneg */
e100_set_speed(0);
e100_set_duplex(autoneg);
e100_set_speed(dev, 0);
e100_set_duplex(dev, autoneg);
break;
case IF_PORT_10BASET:
e100_set_speed(10);
e100_set_duplex(autoneg);
e100_set_speed(dev, 10);
e100_set_duplex(dev, autoneg);
break;
case IF_PORT_100BASET:
case IF_PORT_100BASETX:
e100_set_speed(100);
e100_set_duplex(autoneg);
e100_set_speed(dev, 100);
e100_set_duplex(dev, autoneg);
break;
case IF_PORT_100BASEFX:
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