Commit b5412329 authored by Jeff Garzik's avatar Jeff Garzik

Merge ethtool support and PPC fix into pcnet32 net driver,

from 2.4.x.
Also, remove deprecated SIOCDEVPRIVATE ioctl calls.

Via Dave Jones.
parent 0ec7a504
......@@ -65,7 +65,7 @@ obj-$(CONFIG_DGRS) += dgrs.o
obj-$(CONFIG_RCPCI) += rcpci.o
obj-$(CONFIG_VORTEX) += 3c59x.o
obj-$(CONFIG_NE2K_PCI) += ne2k-pci.o 8390.o
obj-$(CONFIG_PCNET32) += pcnet32.o
obj-$(CONFIG_PCNET32) += pcnet32.o mii.o
obj-$(CONFIG_EEPRO100) += eepro100.o
obj-$(CONFIG_TLAN) += tlan.o
obj-$(CONFIG_EPIC100) += epic100.o mii.o
......
......@@ -21,7 +21,12 @@
*
*************************************************************************/
static const char *version = "pcnet32.c:v1.25kf 26.9.1999 tsbogend@alpha.franken.de\n";
#define DRV_NAME "pcnet32"
#define DRV_VERSION "1.25kf"
#define DRV_RELDATE "17.11.2001"
static const char *version =
DRV_NAME ".c:v" DRV_VERSION " " DRV_RELDATE " tsbogend@alpha.franken.de\n";
#include <linux/module.h>
......@@ -36,10 +41,13 @@ static const char *version = "pcnet32.c:v1.25kf 26.9.1999 tsbogend@alpha.franken
#include <linux/pci.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/ethtool.h>
#include <linux/mii.h>
#include <linux/crc32.h>
#include <asm/bitops.h>
#include <asm/io.h>
#include <asm/dma.h>
#include <asm/uaccess.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
......@@ -283,11 +291,11 @@ struct pcnet32_private {
int shared_irq:1, /* shared irq possible */
ltint:1,
#ifdef DO_DXSUFLO
dxsuflo:1, /* disable transmit stop on uflo */
dxsuflo:1, /* disable transmit stop on uflo */
#endif
full_duplex:1, /* full duplex possible */
mii:1; /* mii port available */
struct net_device *next;
struct mii_if_info mii_if;
};
static int pcnet32_probe_vlbus(int cards_found);
......@@ -302,9 +310,9 @@ static void pcnet32_interrupt(int, void *, struct pt_regs *);
static int pcnet32_close(struct net_device *);
static struct net_device_stats *pcnet32_get_stats(struct net_device *);
static void pcnet32_set_multicast_list(struct net_device *);
#ifdef HAVE_PRIVATE_IOCTL
static int pcnet32_mii_ioctl(struct net_device *, struct ifreq *, int);
#endif
static int pcnet32_ioctl(struct net_device *, struct ifreq *, int);
static int mdio_read(struct net_device *dev, int phy_id, int reg_num);
static void mdio_write(struct net_device *dev, int phy_id, int reg_num, int val);
enum pci_flags_bit {
PCI_USES_IO=1, PCI_USES_MEM=2, PCI_USES_MASTER=4,
......@@ -647,6 +655,13 @@ pcnet32_probe1(unsigned long ioaddr, unsigned char irq_line, int shared, int car
#if defined(__i386__)
printk(KERN_WARNING "%s: Probably a Compaq, using the PROM address of", dev->name);
memcpy(dev->dev_addr, promaddr, 6);
#elif defined(__powerpc__)
if (!is_valid_ether_addr(dev->dev_addr)
&& is_valid_ether_addr(promaddr)) {
printk("\n" KERN_WARNING "%s: using PROM address:",
dev->name);
memcpy(dev->dev_addr, promaddr, 6);
}
#endif
}
}
......@@ -702,7 +717,7 @@ pcnet32_probe1(unsigned long ioaddr, unsigned char irq_line, int shared, int car
dev->priv = lp;
lp->name = chipname;
lp->shared_irq = shared;
lp->full_duplex = fdx;
lp->mii_if.full_duplex = fdx;
#ifdef DO_DXSUFLO
lp->dxsuflo = dxsuflo;
#endif
......@@ -712,6 +727,9 @@ pcnet32_probe1(unsigned long ioaddr, unsigned char irq_line, int shared, int car
lp->options = PCNET32_PORT_ASEL;
else
lp->options = options_mapping[options[card_idx]];
lp->mii_if.dev = dev;
lp->mii_if.mdio_read = mdio_read;
lp->mii_if.mdio_write = mdio_write;
if (fdx && !(lp->options & PCNET32_PORT_ASEL) && full_duplex[card_idx])
lp->options |= PCNET32_PORT_FD;
......@@ -781,9 +799,7 @@ pcnet32_probe1(unsigned long ioaddr, unsigned char irq_line, int shared, int car
dev->stop = &pcnet32_close;
dev->get_stats = &pcnet32_get_stats;
dev->set_multicast_list = &pcnet32_set_multicast_list;
#ifdef HAVE_PRIVATE_IOCTL
dev->do_ioctl = &pcnet32_mii_ioctl;
#endif
dev->do_ioctl = &pcnet32_ioctl;
dev->tx_timeout = pcnet32_tx_timeout;
dev->watchdog_timeo = (HZ >> 1);
......@@ -834,7 +850,7 @@ pcnet32_open(struct net_device *dev)
lp->a.write_bcr (ioaddr, 2, val);
/* handle full duplex setting */
if (lp->full_duplex) {
if (lp->mii_if.full_duplex) {
val = lp->a.read_bcr (ioaddr, 9) & ~3;
if (lp->options & PCNET32_PORT_FD) {
val |= 1;
......@@ -1476,29 +1492,154 @@ static void pcnet32_set_multicast_list(struct net_device *dev)
pcnet32_restart(dev, 0x0042); /* Resume normal operation */
}
#ifdef HAVE_PRIVATE_IOCTL
static int pcnet32_mii_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
static int mdio_read(struct net_device *dev, int phy_id, int reg_num)
{
struct pcnet32_private *lp = dev->priv;
unsigned long ioaddr = dev->base_addr;
u16 val_out;
int phyaddr;
if (!lp->mii)
return 0;
phyaddr = lp->a.read_bcr(ioaddr, 33);
lp->a.write_bcr(ioaddr, 33, ((phy_id & 0x1f) << 5) | (reg_num & 0x1f));
val_out = lp->a.read_bcr(ioaddr, 34);
lp->a.write_bcr(ioaddr, 33, phyaddr);
return val_out;
}
static void mdio_write(struct net_device *dev, int phy_id, int reg_num, int val)
{
struct pcnet32_private *lp = dev->priv;
unsigned long ioaddr = dev->base_addr;
int phyaddr;
if (!lp->mii)
return;
phyaddr = lp->a.read_bcr(ioaddr, 33);
lp->a.write_bcr(ioaddr, 33, ((phy_id & 0x1f) << 5) | (reg_num & 0x1f));
lp->a.write_bcr(ioaddr, 34, val);
lp->a.write_bcr(ioaddr, 33, phyaddr);
}
static int pcnet32_ethtool_ioctl (struct net_device *dev, void *useraddr)
{
struct pcnet32_private *lp = dev->priv;
u32 ethcmd;
int phyaddr = 0;
int phy_id = 0;
unsigned long ioaddr = dev->base_addr;
if (lp->mii) {
phyaddr = lp->a.read_bcr (ioaddr, 33);
phy_id = (phyaddr >> 5) & 0x1f;
lp->mii_if.phy_id = phy_id;
}
if (copy_from_user (&ethcmd, useraddr, sizeof (ethcmd)))
return -EFAULT;
switch (ethcmd) {
case ETHTOOL_GDRVINFO: {
struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO };
strcpy (info.driver, DRV_NAME);
strcpy (info.version, DRV_VERSION);
if (lp->pci_dev)
strcpy (info.bus_info, lp->pci_dev->slot_name);
else
sprintf(info.bus_info, "VLB 0x%lx", dev->base_addr);
if (copy_to_user (useraddr, &info, sizeof (info)))
return -EFAULT;
return 0;
}
/* get settings */
case ETHTOOL_GSET: {
struct ethtool_cmd ecmd = { ETHTOOL_GSET };
spin_lock_irq(&lp->lock);
mii_ethtool_gset(&lp->mii_if, &ecmd);
spin_unlock_irq(&lp->lock);
if (copy_to_user(useraddr, &ecmd, sizeof(ecmd)))
return -EFAULT;
return 0;
}
/* set settings */
case ETHTOOL_SSET: {
int r;
struct ethtool_cmd ecmd;
if (copy_from_user(&ecmd, useraddr, sizeof(ecmd)))
return -EFAULT;
spin_lock_irq(&lp->lock);
r = mii_ethtool_sset(&lp->mii_if, &ecmd);
spin_unlock_irq(&lp->lock);
return r;
}
/* restart autonegotiation */
case ETHTOOL_NWAY_RST: {
return mii_nway_restart(&lp->mii_if);
}
/* get link status */
case ETHTOOL_GLINK: {
struct ethtool_value edata = {ETHTOOL_GLINK};
edata.data = mii_link_ok(&lp->mii_if);
if (copy_to_user(useraddr, &edata, sizeof(edata)))
return -EFAULT;
return 0;
}
/* get message-level */
case ETHTOOL_GMSGLVL: {
struct ethtool_value edata = {ETHTOOL_GMSGLVL};
edata.data = pcnet32_debug;
if (copy_to_user(useraddr, &edata, sizeof(edata)))
return -EFAULT;
return 0;
}
/* set message-level */
case ETHTOOL_SMSGLVL: {
struct ethtool_value edata;
if (copy_from_user(&edata, useraddr, sizeof(edata)))
return -EFAULT;
pcnet32_debug = edata.data;
return 0;
}
default:
break;
}
return -EOPNOTSUPP;
}
static int pcnet32_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
{
unsigned long ioaddr = dev->base_addr;
struct pcnet32_private *lp = dev->priv;
u16 *data = (u16 *)&rq->ifr_data;
struct mii_ioctl_data *data = (struct mii_ioctl_data *)&rq->ifr_data;
int phyaddr = lp->a.read_bcr (ioaddr, 33);
if (cmd == SIOCETHTOOL)
return pcnet32_ethtool_ioctl(dev, (void *) rq->ifr_data);
if (lp->mii) {
switch(cmd) {
case SIOCDEVPRIVATE: /* Get the address of the PHY in use. */
data[0] = (phyaddr >> 5) & 0x1f;
case SIOCGMIIPHY: /* Get address of MII PHY in use. */
data->phy_id = (phyaddr >> 5) & 0x1f;
/* Fall Through */
case SIOCDEVPRIVATE+1: /* Read the specified MII register. */
lp->a.write_bcr (ioaddr, 33, ((data[0] & 0x1f) << 5) | (data[1] & 0x1f));
data[3] = lp->a.read_bcr (ioaddr, 34);
case SIOCGMIIREG: /* Read MII PHY register. */
lp->a.write_bcr (ioaddr, 33, ((data->phy_id & 0x1f) << 5) | (data->reg_num & 0x1f));
data->val_out = lp->a.read_bcr (ioaddr, 34);
lp->a.write_bcr (ioaddr, 33, phyaddr);
return 0;
case SIOCDEVPRIVATE+2: /* Write the specified MII register */
case SIOCSMIIREG: /* Write MII PHY register. */
if (!capable(CAP_NET_ADMIN))
return -EPERM;
lp->a.write_bcr (ioaddr, 33, ((data[0] & 0x1f) << 5) | (data[1] & 0x1f));
lp->a.write_bcr (ioaddr, 34, data[2]);
lp->a.write_bcr (ioaddr, 33, ((data->phy_id & 0x1f) << 5) | (data->reg_num & 0x1f));
lp->a.write_bcr (ioaddr, 34, data->val_in);
lp->a.write_bcr (ioaddr, 33, phyaddr);
return 0;
default:
......@@ -1507,13 +1648,12 @@ static int pcnet32_mii_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
}
return -EOPNOTSUPP;
}
#endif /* HAVE_PRIVATE_IOCTL */
static struct pci_driver pcnet32_driver = {
name: "pcnet32",
probe: pcnet32_probe_pci,
remove: NULL,
id_table: pcnet32_pci_tbl,
name: DRV_NAME,
probe: pcnet32_probe_pci,
remove: NULL,
id_table: pcnet32_pci_tbl,
};
MODULE_PARM(debug, "i");
......
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