Commit 177662fb authored by Jeff Garzik's avatar Jeff Garzik

Merge pobox.com:/spare/repo/netdev-2.6/typhoon

into pobox.com:/spare/repo/net-drivers-2.6
parents deeee3bf f36be730
/* typhoon.c: A Linux Ethernet device driver for 3Com 3CR990 family of NICs */
/*
Written 2002-2003 by David Dillow <dave@thedillows.org>
Written 2002-2004 by David Dillow <dave@thedillows.org>
Based on code written 1998-2000 by Donald Becker <becker@scyld.com> and
Linux 2.2.x driver by David P. McLean <davidpmclean@yahoo.com>.
......@@ -33,8 +33,16 @@
*) Waiting for a command response takes 8ms due to non-preemptable
polling. Only significant for getting stats and creating
SAs, but an ugly wart never the less.
*) I've not tested multicast. I think it works, but reports welcome.
TODO:
*) Doesn't do IPSEC offloading. Yet. Keep yer pants on, it's coming.
*) Add more support for ethtool (especially for NIC stats)
*) Allow disabling of RX checksum offloading
*) Fix MAC changing to work while the interface is up
(Need to put commands on the TX ring, which changes
the locking)
*) Add in FCS to {rx,tx}_bytes, since the hardware doesn't. See
http://oss.sgi.com/cgi-bin/mesg.cgi?a=netdev&i=20031215152211.7003fe8e.rddunlap%40osdl.org
*/
/* Set the copy breakpoint for the copy-only-tiny-frames scheme.
......@@ -85,8 +93,8 @@ static const int multicast_filter_limit = 32;
#define PKT_BUF_SZ 1536
#define DRV_MODULE_NAME "typhoon"
#define DRV_MODULE_VERSION "1.5.3"
#define DRV_MODULE_RELDATE "03/12/15"
#define DRV_MODULE_VERSION "1.5.4"
#define DRV_MODULE_RELDATE "04/09/09"
#define PFX DRV_MODULE_NAME ": "
#define ERR_PFX KERN_ERR PFX
......@@ -411,21 +419,22 @@ typhoon_reset(void __iomem *ioaddr, int wait_type)
out:
writel(TYPHOON_INTR_ALL, ioaddr + TYPHOON_REG_INTR_MASK);
writel(TYPHOON_INTR_ALL, ioaddr + TYPHOON_REG_INTR_STATUS);
udelay(100);
return err;
/* The 3XP seems to need a little extra time to complete the load
* of the sleep image before we can reliably boot it. Failure to
* do this occasionally results in a hung adapter after boot in
* typhoon_init_one() while trying to read the MAC address or
* putting the card to sleep. 3Com's driver waits 5ms, but
* that seems to be overkill -- with a 50usec delay, it survives
* 35000 typhoon_init_one() calls, where it only make it 25-100
* without it.
*
* As it turns out, still occasionally getting a hung adapter,
* so I'm bumping it to 100us.
* that seems to be overkill. However, if we can sleep, we might
* as well give it that much time. Otherwise, we'll give it 500us,
* which should be enough (I've see it work well at 100us, but still
* saw occasional problems.)
*/
if(wait_type == WaitSleep)
msleep(5);
else
udelay(500);
return err;
}
static int
......@@ -689,7 +698,7 @@ typhoon_issue_command(struct typhoon *tp, int num_cmd, struct cmd_desc *cmd,
static void
typhoon_vlan_rx_register(struct net_device *dev, struct vlan_group *grp)
{
struct typhoon *tp = (struct typhoon *) dev->priv;
struct typhoon *tp = netdev_priv(dev);
struct cmd_desc xp_cmd;
int err;
......@@ -727,7 +736,7 @@ typhoon_vlan_rx_register(struct net_device *dev, struct vlan_group *grp)
static void
typhoon_vlan_rx_kill_vid(struct net_device *dev, unsigned short vid)
{
struct typhoon *tp = (struct typhoon *) dev->priv;
struct typhoon *tp = netdev_priv(dev);
spin_lock_bh(&tp->state_lock);
if(tp->vlgrp)
tp->vlgrp->vlan_devices[vid] = NULL;
......@@ -758,7 +767,7 @@ typhoon_tso_fill(struct sk_buff *skb, struct transmit_ring *txRing,
static int
typhoon_start_tx(struct sk_buff *skb, struct net_device *dev)
{
struct typhoon *tp = (struct typhoon *) dev->priv;
struct typhoon *tp = netdev_priv(dev);
struct transmit_ring *txRing;
struct tx_desc *txd, *first_txd;
dma_addr_t skb_dma;
......@@ -909,7 +918,7 @@ typhoon_start_tx(struct sk_buff *skb, struct net_device *dev)
static void
typhoon_set_rx_mode(struct net_device *dev)
{
struct typhoon *tp = (struct typhoon *) dev->priv;
struct typhoon *tp = netdev_priv(dev);
struct cmd_desc xp_cmd;
u32 mc_filter[2];
u16 filter;
......@@ -966,6 +975,9 @@ typhoon_do_get_stats(struct typhoon *tp)
/* 3Com's Linux driver uses txMultipleCollisions as it's
* collisions value, but there is some other collision info as well...
*
* The extra status reported would be a good candidate for
* ethtool_ops->get_{strings,stats}()
*/
stats->tx_packets = le32_to_cpu(s->txPackets);
stats->tx_bytes = le32_to_cpu(s->txBytes);
......@@ -1003,7 +1015,7 @@ typhoon_do_get_stats(struct typhoon *tp)
static struct net_device_stats *
typhoon_get_stats(struct net_device *dev)
{
struct typhoon *tp = (struct typhoon *) dev->priv;
struct typhoon *tp = netdev_priv(dev);
struct net_device_stats *stats = &tp->stats;
struct net_device_stats *saved = &tp->stats_saved;
......@@ -1031,9 +1043,10 @@ typhoon_set_mac_address(struct net_device *dev, void *addr)
return 0;
}
static inline void
typhoon_ethtool_gdrvinfo(struct typhoon *tp, struct ethtool_drvinfo *info)
static void
typhoon_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
{
struct typhoon *tp = netdev_priv(dev);
struct pci_dev *pci_dev = tp->pdev;
struct cmd_desc xp_cmd;
struct resp_desc xp_resp[3];
......@@ -1056,9 +1069,11 @@ typhoon_ethtool_gdrvinfo(struct typhoon *tp, struct ethtool_drvinfo *info)
strcpy(info->bus_info, pci_name(pci_dev));
}
static inline void
typhoon_ethtool_gset(struct typhoon *tp, struct ethtool_cmd *cmd)
static int
typhoon_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
{
struct typhoon *tp = netdev_priv(dev);
cmd->supported = SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full |
SUPPORTED_Autoneg;
......@@ -1108,15 +1123,19 @@ typhoon_ethtool_gset(struct typhoon *tp, struct ethtool_cmd *cmd)
cmd->autoneg = AUTONEG_DISABLE;
cmd->maxtxpkt = 1;
cmd->maxrxpkt = 1;
return 0;
}
static inline int
typhoon_ethtool_sset(struct typhoon *tp, struct ethtool_cmd *cmd)
static int
typhoon_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
{
struct typhoon *tp = netdev_priv(dev);
struct cmd_desc xp_cmd;
int xcvr;
int err;
err = -EINVAL;
if(cmd->autoneg == AUTONEG_ENABLE) {
xcvr = TYPHOON_XCVR_AUTONEG;
} else {
......@@ -1126,23 +1145,23 @@ typhoon_ethtool_sset(struct typhoon *tp, struct ethtool_cmd *cmd)
else if(cmd->speed == SPEED_100)
xcvr = TYPHOON_XCVR_100HALF;
else
return -EINVAL;
goto out;
} else if(cmd->duplex == DUPLEX_FULL) {
if(cmd->speed == SPEED_10)
xcvr = TYPHOON_XCVR_10FULL;
else if(cmd->speed == SPEED_100)
xcvr = TYPHOON_XCVR_100FULL;
else
return -EINVAL;
goto out;
} else
return -EINVAL;
goto out;
}
INIT_COMMAND_NO_RESPONSE(&xp_cmd, TYPHOON_CMD_XCVR_SELECT);
xp_cmd.parm1 = cpu_to_le16(xcvr);
err = typhoon_issue_command(tp, 1, &xp_cmd, 0, NULL);
if(err < 0)
return err;
goto out;
tp->xcvr_select = xcvr;
if(cmd->autoneg == AUTONEG_ENABLE) {
......@@ -1153,93 +1172,80 @@ typhoon_ethtool_sset(struct typhoon *tp, struct ethtool_cmd *cmd)
tp->duplex = cmd->duplex;
}
return 0;
out:
return err;
}
static inline int
typhoon_ethtool_ioctl(struct net_device *dev, void __user *useraddr)
static void
typhoon_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
{
struct typhoon *tp = (struct typhoon *) dev->priv;
u32 ethcmd;
if(copy_from_user(&ethcmd, useraddr, sizeof(ethcmd)))
return -EFAULT;
switch (ethcmd) {
case ETHTOOL_GDRVINFO: {
struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO };
typhoon_ethtool_gdrvinfo(tp, &info);
if(copy_to_user(useraddr, &info, sizeof(info)))
return -EFAULT;
return 0;
}
case ETHTOOL_GSET: {
struct ethtool_cmd cmd = { ETHTOOL_GSET };
typhoon_ethtool_gset(tp, &cmd);
if(copy_to_user(useraddr, &cmd, sizeof(cmd)))
return -EFAULT;
return 0;
}
case ETHTOOL_SSET: {
struct ethtool_cmd cmd;
if(copy_from_user(&cmd, useraddr, sizeof(cmd)))
return -EFAULT;
return typhoon_ethtool_sset(tp, &cmd);
}
case ETHTOOL_GLINK:{
struct ethtool_value edata = { ETHTOOL_GLINK };
edata.data = netif_carrier_ok(dev) ? 1 : 0;
if(copy_to_user(useraddr, &edata, sizeof(edata)))
return -EFAULT;
return 0;
}
case ETHTOOL_GWOL: {
struct ethtool_wolinfo wol = { ETHTOOL_GWOL };
struct typhoon *tp = netdev_priv(dev);
wol->supported = WAKE_PHY | WAKE_MAGIC;
wol->wolopts = 0;
if(tp->wol_events & TYPHOON_WAKE_LINK_EVENT)
wol.wolopts |= WAKE_PHY;
wol->wolopts |= WAKE_PHY;
if(tp->wol_events & TYPHOON_WAKE_MAGIC_PKT)
wol.wolopts |= WAKE_MAGIC;
if(copy_to_user(useraddr, &wol, sizeof(wol)))
return -EFAULT;
return 0;
}
case ETHTOOL_SWOL: {
struct ethtool_wolinfo wol;
wol->wolopts |= WAKE_MAGIC;
memset(&wol->sopass, 0, sizeof(wol->sopass));
}
static int
typhoon_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
{
struct typhoon *tp = netdev_priv(dev);
if(wol->wolopts & ~(WAKE_PHY | WAKE_MAGIC))
return -EINVAL;
if(copy_from_user(&wol, useraddr, sizeof(wol)))
return -EFAULT;
tp->wol_events = 0;
if(wol.wolopts & WAKE_PHY)
if(wol->wolopts & WAKE_PHY)
tp->wol_events |= TYPHOON_WAKE_LINK_EVENT;
if(wol.wolopts & WAKE_MAGIC)
if(wol->wolopts & WAKE_MAGIC)
tp->wol_events |= TYPHOON_WAKE_MAGIC_PKT;
return 0;
}
default:
break;
}
return -EOPNOTSUPP;
return 0;
}
static int
typhoon_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
static u32
typhoon_get_rx_csum(struct net_device *dev)
{
switch (cmd) {
case SIOCETHTOOL:
return typhoon_ethtool_ioctl(dev, ifr->ifr_data);
default:
break;
}
/* For now, we don't allow turning off RX checksums.
*/
return 1;
}
return -EOPNOTSUPP;
static void
typhoon_get_ringparam(struct net_device *dev, struct ethtool_ringparam *ering)
{
ering->rx_max_pending = RXENT_ENTRIES;
ering->rx_mini_max_pending = 0;
ering->rx_jumbo_max_pending = 0;
ering->tx_max_pending = TXLO_ENTRIES - 1;
ering->rx_pending = RXENT_ENTRIES;
ering->rx_mini_pending = 0;
ering->rx_jumbo_pending = 0;
ering->tx_pending = TXLO_ENTRIES - 1;
}
static struct ethtool_ops typhoon_ethtool_ops = {
.get_settings = typhoon_get_settings,
.set_settings = typhoon_set_settings,
.get_drvinfo = typhoon_get_drvinfo,
.get_wol = typhoon_get_wol,
.set_wol = typhoon_set_wol,
.get_link = ethtool_op_get_link,
.get_rx_csum = typhoon_get_rx_csum,
.get_tx_csum = ethtool_op_get_tx_csum,
.set_tx_csum = ethtool_op_set_tx_csum,
.get_sg = ethtool_op_get_sg,
.set_sg = ethtool_op_set_sg,
.get_tso = ethtool_op_get_tso,
.set_tso = ethtool_op_set_tso,
.get_ringparam = typhoon_get_ringparam,
};
static int
typhoon_wait_interrupt(void __iomem *ioaddr)
{
......@@ -1757,7 +1763,7 @@ typhoon_fill_free_ring(struct typhoon *tp)
static int
typhoon_poll(struct net_device *dev, int *total_budget)
{
struct typhoon *tp = (struct typhoon *) dev->priv;
struct typhoon *tp = netdev_priv(dev);
struct typhoon_indexes *indexes = tp->indexes;
int orig_budget = *total_budget;
int budget, work_done, done;
......@@ -2070,7 +2076,7 @@ typhoon_stop_runtime(struct typhoon *tp, int wait_type)
static void
typhoon_tx_timeout(struct net_device *dev)
{
struct typhoon *tp = (struct typhoon *) dev->priv;
struct typhoon *tp = netdev_priv(dev);
if(typhoon_reset(tp->ioaddr, WaitNoSleep) < 0) {
printk(KERN_WARNING "%s: could not reset in tx timeout\n",
......@@ -2100,7 +2106,7 @@ typhoon_tx_timeout(struct net_device *dev)
static int
typhoon_open(struct net_device *dev)
{
struct typhoon *tp = (struct typhoon *) dev->priv;
struct typhoon *tp = netdev_priv(dev);
int err;
err = typhoon_wakeup(tp, WaitSleep);
......@@ -2142,7 +2148,7 @@ typhoon_open(struct net_device *dev)
static int
typhoon_close(struct net_device *dev)
{
struct typhoon *tp = (struct typhoon *) dev->priv;
struct typhoon *tp = netdev_priv(dev);
netif_stop_queue(dev);
......@@ -2170,7 +2176,7 @@ static int
typhoon_resume(struct pci_dev *pdev)
{
struct net_device *dev = pci_get_drvdata(pdev);
struct typhoon *tp = (struct typhoon *) dev->priv;
struct typhoon *tp = netdev_priv(dev);
/* If we're down, resume when we are upped.
*/
......@@ -2202,7 +2208,7 @@ static int
typhoon_suspend(struct pci_dev *pdev, u32 state)
{
struct net_device *dev = pci_get_drvdata(pdev);
struct typhoon *tp = (struct typhoon *) dev->priv;
struct typhoon *tp = netdev_priv(dev);
struct cmd_desc xp_cmd;
/* If we're down, we're already suspended.
......@@ -2306,17 +2312,17 @@ typhoon_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
goto error_out_dev;
}
/* If we transitioned from D3->D0 in pci_enable_device(),
* we lost our configuration and need to restore it to the
* conditions at boot.
*/
pci_restore_state(pdev, NULL);
err = pci_set_mwi(pdev);
if(err < 0) {
printk(ERR_PFX "%s: unable to set MWI\n", pci_name(pdev));
goto error_out_disable;
}
err = pci_set_dma_mask(pdev, 0xffffffffULL);
err = pci_set_dma_mask(pdev, DMA_32BIT_MASK);
if(err < 0) {
printk(ERR_PFX "%s: No usable DMA configuration\n",
pci_name(pdev));
goto error_out_dev;
goto error_out_mwi;
}
/* sanity checks, resource #1 is our mmio area
......@@ -2326,25 +2332,22 @@ typhoon_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
"%s: region #1 not a PCI MMIO resource, aborting\n",
pci_name(pdev));
err = -ENODEV;
goto error_out_dev;
goto error_out_mwi;
}
if(pci_resource_len(pdev, 1) < 128) {
printk(ERR_PFX "%s: Invalid PCI MMIO region size, aborting\n",
pci_name(pdev));
err = -ENODEV;
goto error_out_dev;
goto error_out_mwi;
}
err = pci_request_regions(pdev, "typhoon");
if(err < 0) {
printk(ERR_PFX "%s: could not request regions\n",
pci_name(pdev));
goto error_out_dev;
goto error_out_mwi;
}
pci_set_master(pdev);
pci_set_mwi(pdev);
/* map our MMIO region
*/
ioaddr = pci_resource_start(pdev, 1);
......@@ -2368,7 +2371,7 @@ typhoon_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
}
dev->irq = pdev->irq;
tp = dev->priv;
tp = netdev_priv(dev);
tp->shared = (struct typhoon_shared *) shared;
tp->shared_dma = shared_dma;
tp->pdev = pdev;
......@@ -2393,6 +2396,11 @@ typhoon_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
goto error_out_dma;
}
/* Now that we've reset the 3XP and are sure it's not going to
* write all over memory, enable bus mastering.
*/
pci_set_master(pdev);
/* dev->name is not valid until we register, but we need to
* use some common routines to initialize the card. So that those
* routines print the right name, we keep our oun pointer to the name
......@@ -2462,11 +2470,11 @@ typhoon_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
dev->set_multicast_list = typhoon_set_rx_mode;
dev->tx_timeout = typhoon_tx_timeout;
dev->poll = typhoon_poll;
dev->ethtool_ops = &typhoon_ethtool_ops;
dev->weight = 16;
dev->watchdog_timeo = TX_TIMEOUT;
dev->get_stats = typhoon_get_stats;
dev->set_mac_address = typhoon_set_mac_address;
dev->do_ioctl = typhoon_ioctl;
dev->vlan_rx_register = typhoon_vlan_rx_register;
dev->vlan_rx_kill_vid = typhoon_vlan_rx_kill_vid;
......@@ -2529,6 +2537,10 @@ typhoon_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
iounmap(ioaddr_mapped);
error_out_regions:
pci_release_regions(pdev);
error_out_mwi:
pci_clear_mwi(pdev);
error_out_disable:
pci_disable_device(pdev);
error_out_dev:
free_netdev(dev);
error_out:
......@@ -2539,7 +2551,7 @@ static void __devexit
typhoon_remove_one(struct pci_dev *pdev)
{
struct net_device *dev = pci_get_drvdata(pdev);
struct typhoon *tp = (struct typhoon *) (dev->priv);
struct typhoon *tp = netdev_priv(dev);
unregister_netdev(dev);
pci_set_power_state(pdev, 0);
......@@ -2549,6 +2561,7 @@ typhoon_remove_one(struct pci_dev *pdev)
pci_free_consistent(pdev, sizeof(struct typhoon_shared),
tp->shared, tp->shared_dma);
pci_release_regions(pdev);
pci_clear_mwi(pdev);
pci_disable_device(pdev);
pci_set_drvdata(pdev, NULL);
free_netdev(dev);
......
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