*
*************************************************************************/
-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>
#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>
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);
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,
#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
}
}
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
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;
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);
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;
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 (ðcmd, 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:
}
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");