/* 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>.
*) 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.
*/
static int rx_copybreak = 200;
+/* Should we use MMIO or Port IO?
+ * 0: Port IO
+ * 1: MMIO
+ * 2: Try MMIO, fallback to Port IO
+ */
+static unsigned int use_mmio = 2;
+
/* end user-configurable values */
/* Maximum number of multicast addresses to filter (vs. rx-all-multicast).
#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.7"
+#define DRV_MODULE_RELDATE "05/01/07"
#define PFX DRV_MODULE_NAME ": "
#define ERR_PFX KERN_ERR PFX
#include <linux/ethtool.h>
#include <linux/if_vlan.h>
#include <linux/crc32.h>
+#include <linux/bitops.h>
#include <asm/processor.h>
-#include <asm/bitops.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <linux/in6.h>
#include <asm/checksum.h>
#include <linux/version.h>
+#include <linux/dma-mapping.h>
#include "typhoon.h"
#include "typhoon-firmware.h"
"typhoon.c: version " DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")\n";
MODULE_AUTHOR("David Dillow <dave@thedillows.org>");
+MODULE_VERSION(DRV_MODULE_VERSION);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("3Com Typhoon Family (3C990, 3CR990, and variants)");
-MODULE_PARM(rx_copybreak, "i");
+MODULE_PARM_DESC(rx_copybreak, "Packets smaller than this are copied and "
+ "the buffer given back to the NIC. Default "
+ "is 200.");
+MODULE_PARM_DESC(use_mmio, "Use MMIO (1) or PIO(0) to access the NIC. "
+ "Default is to try MMIO and fallback to PIO.");
+module_param(rx_copybreak, int, 0);
+module_param(use_mmio, int, 0);
#if defined(NETIF_F_TSO) && MAX_SKB_FRAGS > 32
#warning Typhoon only supports 32 entries in its SG list for TSO, disabling TSO
u16 xcvr_select;
u16 wol_events;
u32 offload;
- u32 pci_state[16];
/* unused stuff (future use) */
int capabilities;
* cannot pass a read, so this forces current writes to post.
*/
#define typhoon_post_pci_writes(x) \
- do { readl(x + TYPHOON_REG_HEARTBEAT); } while(0)
+ do { if(likely(use_mmio)) ioread32(x+TYPHOON_REG_HEARTBEAT); } while(0)
/* We'll wait up to six seconds for a reset, and half a second normally.
*/
else
timeout = TYPHOON_RESET_TIMEOUT_SLEEP;
- writel(TYPHOON_INTR_ALL, ioaddr + TYPHOON_REG_INTR_MASK);
- writel(TYPHOON_INTR_ALL, ioaddr + TYPHOON_REG_INTR_STATUS);
+ iowrite32(TYPHOON_INTR_ALL, ioaddr + TYPHOON_REG_INTR_MASK);
+ iowrite32(TYPHOON_INTR_ALL, ioaddr + TYPHOON_REG_INTR_STATUS);
- writel(TYPHOON_RESET_ALL, ioaddr + TYPHOON_REG_SOFT_RESET);
+ iowrite32(TYPHOON_RESET_ALL, ioaddr + TYPHOON_REG_SOFT_RESET);
typhoon_post_pci_writes(ioaddr);
udelay(1);
- writel(TYPHOON_RESET_NONE, ioaddr + TYPHOON_REG_SOFT_RESET);
+ iowrite32(TYPHOON_RESET_NONE, ioaddr + TYPHOON_REG_SOFT_RESET);
if(wait_type != NoWait) {
for(i = 0; i < timeout; i++) {
- if(readl(ioaddr + TYPHOON_REG_STATUS) ==
+ if(ioread32(ioaddr + TYPHOON_REG_STATUS) ==
TYPHOON_STATUS_WAITING_FOR_HOST)
goto out;
- if(wait_type == WaitSleep) {
- set_current_state(TASK_UNINTERRUPTIBLE);
- schedule_timeout(1);
- } else
+ if(wait_type == WaitSleep)
+ schedule_timeout_uninterruptible(1);
+ else
udelay(TYPHOON_UDELAY);
}
}
out:
- writel(TYPHOON_INTR_ALL, ioaddr + TYPHOON_REG_INTR_MASK);
- writel(TYPHOON_INTR_ALL, ioaddr + TYPHOON_REG_INTR_STATUS);
- udelay(100);
- return err;
+ iowrite32(TYPHOON_INTR_ALL, ioaddr + TYPHOON_REG_INTR_MASK);
+ iowrite32(TYPHOON_INTR_ALL, ioaddr + TYPHOON_REG_INTR_STATUS);
/* 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
int i, err = 0;
for(i = 0; i < TYPHOON_WAIT_TIMEOUT; i++) {
- if(readl(ioaddr + TYPHOON_REG_STATUS) == wait_value)
+ if(ioread32(ioaddr + TYPHOON_REG_STATUS) == wait_value)
goto out;
udelay(TYPHOON_UDELAY);
}
INIT_COMMAND_NO_RESPONSE(cmd, TYPHOON_CMD_HELLO_RESP);
smp_wmb();
- writel(ring->lastWrite, tp->ioaddr + TYPHOON_REG_CMD_READY);
+ iowrite32(ring->lastWrite, tp->ioaddr + TYPHOON_REG_CMD_READY);
spin_unlock(&tp->command_lock);
}
}
/* "I feel a presence... another warrior is on the the mesa."
*/
wmb();
- writel(ring->lastWrite, tp->ioaddr + TYPHOON_REG_CMD_READY);
+ iowrite32(ring->lastWrite, tp->ioaddr + TYPHOON_REG_CMD_READY);
typhoon_post_pci_writes(tp->ioaddr);
if((cmd->flags & TYPHOON_CMD_RESPOND) == 0)
* is the case.
*/
if(indexes->respCleared != indexes->respReady)
- writel(1, tp->ioaddr + TYPHOON_REG_SELF_INTERRUPT);
+ iowrite32(1, tp->ioaddr + TYPHOON_REG_SELF_INTERRUPT);
}
spin_unlock(&tp->command_lock);
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;
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;
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;
/* Kick the 3XP
*/
wmb();
- writel(txRing->lastWrite, tp->tx_ioaddr + txRing->writeRegister);
+ iowrite32(txRing->lastWrite, tp->tx_ioaddr + txRing->writeRegister);
dev->trans_start = jiffies;
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;
/* 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);
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;
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];
if(typhoon_issue_command(tp, 1, &xp_cmd, 3, xp_resp) < 0) {
strcpy(info->fw_version, "Unknown runtime");
} else {
- strncpy(info->fw_version, (char *) &xp_resp[1], 32);
- info->fw_version[31] = 0;
+ u32 sleep_ver = xp_resp[0].parm2;
+ snprintf(info->fw_version, 32, "%02x.%03x.%03x",
+ sleep_ver >> 24, (sleep_ver >> 12) & 0xfff,
+ sleep_ver & 0xfff);
}
}
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;
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 {
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) {
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(ðcmd, 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 };
+ 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;
+ if(tp->wol_events & TYPHOON_WAKE_MAGIC_PKT)
+ wol->wolopts |= WAKE_MAGIC;
+ memset(&wol->sopass, 0, sizeof(wol->sopass));
+}
- 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;
+static int
+typhoon_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
+{
+ struct typhoon *tp = netdev_priv(dev);
- return typhoon_ethtool_sset(tp, &cmd);
- }
- case ETHTOOL_GLINK:{
- struct ethtool_value edata = { ETHTOOL_GLINK };
+ if(wol->wolopts & ~(WAKE_PHY | WAKE_MAGIC))
+ return -EINVAL;
- 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 };
-
- if(tp->wol_events & TYPHOON_WAKE_LINK_EVENT)
- 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;
-
- if(copy_from_user(&wol, useraddr, sizeof(wol)))
- return -EFAULT;
- tp->wol_events = 0;
- if(wol.wolopts & WAKE_PHY)
- tp->wol_events |= TYPHOON_WAKE_LINK_EVENT;
- if(wol.wolopts & WAKE_MAGIC)
- tp->wol_events |= TYPHOON_WAKE_MAGIC_PKT;
- return 0;
- }
- default:
- break;
- }
+ tp->wol_events = 0;
+ if(wol->wolopts & WAKE_PHY)
+ tp->wol_events |= TYPHOON_WAKE_LINK_EVENT;
+ if(wol->wolopts & WAKE_MAGIC)
+ tp->wol_events |= TYPHOON_WAKE_MAGIC_PKT;
- 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)
{
int i, err = 0;
for(i = 0; i < TYPHOON_WAIT_TIMEOUT; i++) {
- if(readl(ioaddr + TYPHOON_REG_INTR_STATUS) &
+ if(ioread32(ioaddr + TYPHOON_REG_INTR_STATUS) &
TYPHOON_INTR_BOOTCMD)
goto out;
udelay(TYPHOON_UDELAY);
err = -ETIMEDOUT;
out:
- writel(TYPHOON_INTR_BOOTCMD, ioaddr + TYPHOON_REG_INTR_STATUS);
+ iowrite32(TYPHOON_INTR_BOOTCMD, ioaddr + TYPHOON_REG_INTR_STATUS);
return err;
}
goto err_out;
}
- irqEnabled = readl(ioaddr + TYPHOON_REG_INTR_ENABLE);
- writel(irqEnabled | TYPHOON_INTR_BOOTCMD,
+ irqEnabled = ioread32(ioaddr + TYPHOON_REG_INTR_ENABLE);
+ iowrite32(irqEnabled | TYPHOON_INTR_BOOTCMD,
ioaddr + TYPHOON_REG_INTR_ENABLE);
- irqMasked = readl(ioaddr + TYPHOON_REG_INTR_MASK);
- writel(irqMasked | TYPHOON_INTR_BOOTCMD,
+ irqMasked = ioread32(ioaddr + TYPHOON_REG_INTR_MASK);
+ iowrite32(irqMasked | TYPHOON_INTR_BOOTCMD,
ioaddr + TYPHOON_REG_INTR_MASK);
err = -ETIMEDOUT;
numSections = le32_to_cpu(fHdr->numSections);
load_addr = le32_to_cpu(fHdr->startAddr);
- writel(TYPHOON_INTR_BOOTCMD, ioaddr + TYPHOON_REG_INTR_STATUS);
- writel(load_addr, ioaddr + TYPHOON_REG_DOWNLOAD_BOOT_ADDR);
+ iowrite32(TYPHOON_INTR_BOOTCMD, ioaddr + TYPHOON_REG_INTR_STATUS);
+ iowrite32(load_addr, ioaddr + TYPHOON_REG_DOWNLOAD_BOOT_ADDR);
hmac = le32_to_cpu(fHdr->hmacDigest[0]);
- writel(hmac, ioaddr + TYPHOON_REG_DOWNLOAD_HMAC_0);
+ iowrite32(hmac, ioaddr + TYPHOON_REG_DOWNLOAD_HMAC_0);
hmac = le32_to_cpu(fHdr->hmacDigest[1]);
- writel(hmac, ioaddr + TYPHOON_REG_DOWNLOAD_HMAC_1);
+ iowrite32(hmac, ioaddr + TYPHOON_REG_DOWNLOAD_HMAC_1);
hmac = le32_to_cpu(fHdr->hmacDigest[2]);
- writel(hmac, ioaddr + TYPHOON_REG_DOWNLOAD_HMAC_2);
+ iowrite32(hmac, ioaddr + TYPHOON_REG_DOWNLOAD_HMAC_2);
hmac = le32_to_cpu(fHdr->hmacDigest[3]);
- writel(hmac, ioaddr + TYPHOON_REG_DOWNLOAD_HMAC_3);
+ iowrite32(hmac, ioaddr + TYPHOON_REG_DOWNLOAD_HMAC_3);
hmac = le32_to_cpu(fHdr->hmacDigest[4]);
- writel(hmac, ioaddr + TYPHOON_REG_DOWNLOAD_HMAC_4);
+ iowrite32(hmac, ioaddr + TYPHOON_REG_DOWNLOAD_HMAC_4);
typhoon_post_pci_writes(ioaddr);
- writel(TYPHOON_BOOTCMD_RUNTIME_IMAGE, ioaddr + TYPHOON_REG_COMMAND);
+ iowrite32(TYPHOON_BOOTCMD_RUNTIME_IMAGE, ioaddr + TYPHOON_REG_COMMAND);
image_data += sizeof(struct typhoon_file_header);
- /* The readl() in typhoon_wait_interrupt() will force the
+ /* The ioread32() in typhoon_wait_interrupt() will force the
* last write to the command register to post, so
* we don't need a typhoon_post_pci_writes() after it.
*/
len = min_t(u32, section_len, PAGE_SIZE);
if(typhoon_wait_interrupt(ioaddr) < 0 ||
- readl(ioaddr + TYPHOON_REG_STATUS) !=
+ ioread32(ioaddr + TYPHOON_REG_STATUS) !=
TYPHOON_STATUS_WAITING_FOR_SEGMENT) {
printk(KERN_ERR "%s: segment ready timeout\n",
tp->name);
csum = csum_fold(csum);
csum = le16_to_cpu(csum);
- writel(len, ioaddr + TYPHOON_REG_BOOT_LENGTH);
- writel(csum, ioaddr + TYPHOON_REG_BOOT_CHECKSUM);
- writel(load_addr, ioaddr + TYPHOON_REG_BOOT_DEST_ADDR);
- writel(0, ioaddr + TYPHOON_REG_BOOT_DATA_HI);
- writel(dpage_dma, ioaddr + TYPHOON_REG_BOOT_DATA_LO);
+ iowrite32(len, ioaddr + TYPHOON_REG_BOOT_LENGTH);
+ iowrite32(csum, ioaddr + TYPHOON_REG_BOOT_CHECKSUM);
+ iowrite32(load_addr,
+ ioaddr + TYPHOON_REG_BOOT_DEST_ADDR);
+ iowrite32(0, ioaddr + TYPHOON_REG_BOOT_DATA_HI);
+ iowrite32(dpage_dma, ioaddr + TYPHOON_REG_BOOT_DATA_LO);
typhoon_post_pci_writes(ioaddr);
- writel(TYPHOON_BOOTCMD_SEG_AVAILABLE,
+ iowrite32(TYPHOON_BOOTCMD_SEG_AVAILABLE,
ioaddr + TYPHOON_REG_COMMAND);
image_data += len;
}
if(typhoon_wait_interrupt(ioaddr) < 0 ||
- readl(ioaddr + TYPHOON_REG_STATUS) !=
+ ioread32(ioaddr + TYPHOON_REG_STATUS) !=
TYPHOON_STATUS_WAITING_FOR_SEGMENT) {
printk(KERN_ERR "%s: final segment ready timeout\n", tp->name);
goto err_out_irq;
}
- writel(TYPHOON_BOOTCMD_DNLD_COMPLETE, ioaddr + TYPHOON_REG_COMMAND);
+ iowrite32(TYPHOON_BOOTCMD_DNLD_COMPLETE, ioaddr + TYPHOON_REG_COMMAND);
if(typhoon_wait_status(ioaddr, TYPHOON_STATUS_WAITING_FOR_BOOT) < 0) {
printk(KERN_ERR "%s: boot ready timeout, status 0x%0x\n",
- tp->name, readl(ioaddr + TYPHOON_REG_STATUS));
+ tp->name, ioread32(ioaddr + TYPHOON_REG_STATUS));
goto err_out_irq;
}
err = 0;
err_out_irq:
- writel(irqMasked, ioaddr + TYPHOON_REG_INTR_MASK);
- writel(irqEnabled, ioaddr + TYPHOON_REG_INTR_ENABLE);
+ iowrite32(irqMasked, ioaddr + TYPHOON_REG_INTR_MASK);
+ iowrite32(irqEnabled, ioaddr + TYPHOON_REG_INTR_ENABLE);
pci_free_consistent(pdev, PAGE_SIZE, dpage, dpage_dma);
goto out_timeout;
}
- writel(0, ioaddr + TYPHOON_REG_BOOT_RECORD_ADDR_HI);
- writel(tp->shared_dma, ioaddr + TYPHOON_REG_BOOT_RECORD_ADDR_LO);
+ iowrite32(0, ioaddr + TYPHOON_REG_BOOT_RECORD_ADDR_HI);
+ iowrite32(tp->shared_dma, ioaddr + TYPHOON_REG_BOOT_RECORD_ADDR_LO);
typhoon_post_pci_writes(ioaddr);
- writel(TYPHOON_BOOTCMD_REG_BOOT_RECORD, ioaddr + TYPHOON_REG_COMMAND);
+ iowrite32(TYPHOON_BOOTCMD_REG_BOOT_RECORD,
+ ioaddr + TYPHOON_REG_COMMAND);
if(typhoon_wait_status(ioaddr, TYPHOON_STATUS_RUNNING) < 0) {
printk(KERN_ERR "%s: boot finish timeout (status 0x%x)\n",
- tp->name, readl(ioaddr + TYPHOON_REG_STATUS));
+ tp->name, ioread32(ioaddr + TYPHOON_REG_STATUS));
goto out_timeout;
}
/* Clear the Transmit and Command ready registers
*/
- writel(0, ioaddr + TYPHOON_REG_TX_HI_READY);
- writel(0, ioaddr + TYPHOON_REG_CMD_READY);
- writel(0, ioaddr + TYPHOON_REG_TX_LO_READY);
+ iowrite32(0, ioaddr + TYPHOON_REG_TX_HI_READY);
+ iowrite32(0, ioaddr + TYPHOON_REG_CMD_READY);
+ iowrite32(0, ioaddr + TYPHOON_REG_TX_LO_READY);
typhoon_post_pci_writes(ioaddr);
- writel(TYPHOON_BOOTCMD_BOOT, ioaddr + TYPHOON_REG_COMMAND);
+ iowrite32(TYPHOON_BOOTCMD_BOOT, ioaddr + TYPHOON_REG_COMMAND);
return 0;
#endif
skb->dev = tp->dev;
- dma_addr = pci_map_single(tp->pdev, skb->tail,
+ dma_addr = pci_map_single(tp->pdev, skb->data,
PKT_BUF_SZ, PCI_DMA_FROMDEVICE);
/* Since no card does 64 bit DAC, the high bits will never
skb = rxb->skb;
dma_addr = rxb->dma_addr;
- rxaddr += sizeof(struct rx_desc);
- rxaddr %= RX_ENTRIES * sizeof(struct rx_desc);
+ typhoon_inc_rx_index(&rxaddr, 1);
if(rx->flags & TYPHOON_RX_ERROR) {
typhoon_recycle_rx_skb(tp, idx);
pci_dma_sync_single_for_cpu(tp->pdev, dma_addr,
PKT_BUF_SZ,
PCI_DMA_FROMDEVICE);
- eth_copy_and_sum(new_skb, skb->tail, pkt_len, 0);
+ eth_copy_and_sum(new_skb, skb->data, pkt_len, 0);
pci_dma_sync_single_for_device(tp->pdev, dma_addr,
PKT_BUF_SZ,
PCI_DMA_FROMDEVICE);
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;
if(done) {
netif_rx_complete(dev);
- writel(TYPHOON_INTR_NONE, tp->ioaddr + TYPHOON_REG_INTR_MASK);
+ iowrite32(TYPHOON_INTR_NONE,
+ tp->ioaddr + TYPHOON_REG_INTR_MASK);
typhoon_post_pci_writes(tp->ioaddr);
}
void __iomem *ioaddr = tp->ioaddr;
u32 intr_status;
- intr_status = readl(ioaddr + TYPHOON_REG_INTR_STATUS);
+ intr_status = ioread32(ioaddr + TYPHOON_REG_INTR_STATUS);
if(!(intr_status & TYPHOON_INTR_HOST_INT))
return IRQ_NONE;
- writel(intr_status, ioaddr + TYPHOON_REG_INTR_STATUS);
+ iowrite32(intr_status, ioaddr + TYPHOON_REG_INTR_STATUS);
if(netif_rx_schedule_prep(dev)) {
- writel(TYPHOON_INTR_ALL, ioaddr + TYPHOON_REG_INTR_MASK);
+ iowrite32(TYPHOON_INTR_ALL, ioaddr + TYPHOON_REG_INTR_MASK);
typhoon_post_pci_writes(ioaddr);
__netif_rx_schedule(dev);
} else {
}
static int
-typhoon_sleep(struct typhoon *tp, int state, u16 events)
+typhoon_sleep(struct typhoon *tp, pci_power_t state, u16 events)
{
struct pci_dev *pdev = tp->pdev;
void __iomem *ioaddr = tp->ioaddr;
struct pci_dev *pdev = tp->pdev;
void __iomem *ioaddr = tp->ioaddr;
- pci_set_power_state(pdev, 0);
- pci_restore_state(pdev, tp->pci_state);
+ pci_set_power_state(pdev, PCI_D0);
+ pci_restore_state(pdev);
/* Post 2.x.x versions of the Sleep Image require a reset before
* we can download the Runtime Image. But let's not make users of
* the old firmware pay for the reset.
*/
- writel(TYPHOON_BOOTCMD_WAKEUP, ioaddr + TYPHOON_REG_COMMAND);
+ iowrite32(TYPHOON_BOOTCMD_WAKEUP, ioaddr + TYPHOON_REG_COMMAND);
if(typhoon_wait_status(ioaddr, TYPHOON_STATUS_WAITING_FOR_HOST) < 0 ||
(tp->capabilities & TYPHOON_WAKEUP_NEEDS_RESET))
return typhoon_reset(ioaddr, wait_type);
tp->card_state = Running;
smp_wmb();
- writel(TYPHOON_INTR_ENABLE_ALL, ioaddr + TYPHOON_REG_INTR_ENABLE);
- writel(TYPHOON_INTR_NONE, ioaddr + TYPHOON_REG_INTR_MASK);
+ iowrite32(TYPHOON_INTR_ENABLE_ALL, ioaddr + TYPHOON_REG_INTR_ENABLE);
+ iowrite32(TYPHOON_INTR_NONE, ioaddr + TYPHOON_REG_INTR_MASK);
typhoon_post_pci_writes(ioaddr);
return 0;
* when called with !netif_running(). This will be posted
* when we force the posting of the command.
*/
- writel(TYPHOON_INTR_NONE, ioaddr + TYPHOON_REG_INTR_ENABLE);
+ iowrite32(TYPHOON_INTR_NONE, ioaddr + TYPHOON_REG_INTR_ENABLE);
INIT_COMMAND_NO_RESPONSE(&xp_cmd, TYPHOON_CMD_RX_DISABLE);
typhoon_issue_command(tp, 1, &xp_cmd, 0, NULL);
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",
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);
goto out;
}
- if(typhoon_sleep(tp, 3, 0) < 0)
+ if(typhoon_sleep(tp, PCI_D3hot, 0) < 0)
printk(KERN_ERR "%s: unable to go back to sleep\n", dev->name);
out:
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);
if(typhoon_boot_3XP(tp, TYPHOON_STATUS_WAITING_FOR_HOST) < 0)
printk(KERN_ERR "%s: unable to boot sleep image\n", dev->name);
- if(typhoon_sleep(tp, 3, 0) < 0)
+ if(typhoon_sleep(tp, PCI_D3hot, 0) < 0)
printk(KERN_ERR "%s: unable to put card to sleep\n", dev->name);
return 0;
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.
*/
}
static int
-typhoon_suspend(struct pci_dev *pdev, u32 state)
+typhoon_suspend(struct pci_dev *pdev, pm_message_t 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.
goto need_resume;
}
- if(typhoon_sleep(tp, state, tp->wol_events) < 0) {
+ if(typhoon_sleep(tp, pci_choose_state(pdev, state), tp->wol_events) < 0) {
printk(KERN_ERR "%s: unable to put card to sleep\n", dev->name);
goto need_resume;
}
}
static int
-typhoon_enable_wake(struct pci_dev *pdev, u32 state, int enable)
+typhoon_enable_wake(struct pci_dev *pdev, pci_power_t state, int enable)
{
return pci_enable_wake(pdev, state, enable);
}
#endif
+static int __devinit
+typhoon_test_mmio(struct pci_dev *pdev)
+{
+ void __iomem *ioaddr = pci_iomap(pdev, 1, 128);
+ int mode = 0;
+ u32 val;
+
+ if(!ioaddr)
+ goto out;
+
+ if(ioread32(ioaddr + TYPHOON_REG_STATUS) !=
+ TYPHOON_STATUS_WAITING_FOR_HOST)
+ goto out_unmap;
+
+ iowrite32(TYPHOON_INTR_ALL, ioaddr + TYPHOON_REG_INTR_MASK);
+ iowrite32(TYPHOON_INTR_ALL, ioaddr + TYPHOON_REG_INTR_STATUS);
+ iowrite32(TYPHOON_INTR_ALL, ioaddr + TYPHOON_REG_INTR_ENABLE);
+
+ /* Ok, see if we can change our interrupt status register by
+ * sending ourselves an interrupt. If so, then MMIO works.
+ * The 50usec delay is arbitrary -- it could probably be smaller.
+ */
+ val = ioread32(ioaddr + TYPHOON_REG_INTR_STATUS);
+ if((val & TYPHOON_INTR_SELF) == 0) {
+ iowrite32(1, ioaddr + TYPHOON_REG_SELF_INTERRUPT);
+ ioread32(ioaddr + TYPHOON_REG_INTR_STATUS);
+ udelay(50);
+ val = ioread32(ioaddr + TYPHOON_REG_INTR_STATUS);
+ if(val & TYPHOON_INTR_SELF)
+ mode = 1;
+ }
+
+ iowrite32(TYPHOON_INTR_ALL, ioaddr + TYPHOON_REG_INTR_MASK);
+ iowrite32(TYPHOON_INTR_ALL, ioaddr + TYPHOON_REG_INTR_STATUS);
+ iowrite32(TYPHOON_INTR_NONE, ioaddr + TYPHOON_REG_INTR_ENABLE);
+ ioread32(ioaddr + TYPHOON_REG_INTR_STATUS);
+
+out_unmap:
+ pci_iounmap(pdev, ioaddr);
+
+out:
+ if(!mode)
+ printk(KERN_INFO PFX "falling back to port IO\n");
+ return mode;
+}
+
static int __devinit
typhoon_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
{
struct net_device *dev;
struct typhoon *tp;
int card_id = (int) ent->driver_data;
- unsigned long ioaddr;
- void __iomem *ioaddr_mapped;
+ void __iomem *ioaddr;
void *shared;
dma_addr_t shared_dma;
struct cmd_desc xp_cmd;
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
+ /* sanity checks on IO and MMIO BARs
*/
+ if(!(pci_resource_flags(pdev, 0) & IORESOURCE_IO)) {
+ printk(ERR_PFX
+ "%s: region #1 not a PCI IO resource, aborting\n",
+ pci_name(pdev));
+ err = -ENODEV;
+ goto error_out_mwi;
+ }
+ if(pci_resource_len(pdev, 0) < 128) {
+ printk(ERR_PFX "%s: Invalid PCI IO region size, aborting\n",
+ pci_name(pdev));
+ err = -ENODEV;
+ goto error_out_mwi;
+ }
if(!(pci_resource_flags(pdev, 1) & IORESOURCE_MEM)) {
printk(ERR_PFX
"%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
+ /* map our registers
*/
- ioaddr = pci_resource_start(pdev, 1);
- ioaddr_mapped = ioremap(ioaddr, 128);
- if (!ioaddr_mapped) {
- printk(ERR_PFX "%s: cannot remap MMIO, aborting\n",
+ if(use_mmio != 0 && use_mmio != 1)
+ use_mmio = typhoon_test_mmio(pdev);
+
+ ioaddr = pci_iomap(pdev, use_mmio, 128);
+ if (!ioaddr) {
+ printk(ERR_PFX "%s: cannot remap registers, aborting\n",
pci_name(pdev));
err = -EIO;
goto error_out_regions;
}
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;
tp->tx_pdev = pdev;
- tp->ioaddr = ioaddr_mapped;
- tp->tx_ioaddr = ioaddr_mapped;
+ tp->ioaddr = ioaddr;
+ tp->tx_ioaddr = ioaddr;
tp->dev = dev;
- /* need to be able to restore PCI state after a suspend */
- pci_save_state(pdev, tp->pci_state);
-
/* Init sequence:
* 1) Reset the adapter to clear any bad juju
* 2) Reload the sleep image
* 4) Get the hardware address.
* 5) Put the card to sleep.
*/
- if (typhoon_reset(ioaddr_mapped, WaitSleep) < 0) {
+ if (typhoon_reset(ioaddr, WaitSleep) < 0) {
printk(ERR_PFX "%s: could not reset 3XP\n", pci_name(pdev));
err = -EIO;
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, and save our
+ * state for resuming after a suspend.
+ */
+ pci_set_master(pdev);
+ pci_save_state(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
INIT_COMMAND_WITH_RESPONSE(&xp_cmd, TYPHOON_CMD_READ_VERSIONS);
if(typhoon_issue_command(tp, 1, &xp_cmd, 3, xp_resp) < 0) {
printk(ERR_PFX "%s: Could not get Sleep Image version\n",
- pdev->slot_name);
+ pci_name(pdev));
goto error_out_reset;
}
if(xp_resp[0].numDesc != 0)
tp->capabilities |= TYPHOON_WAKEUP_NEEDS_RESET;
- if(typhoon_sleep(tp, 3, 0) < 0) {
+ if(typhoon_sleep(tp, PCI_D3hot, 0) < 0) {
printk(ERR_PFX "%s: cannot put adapter to sleep\n",
pci_name(pdev));
err = -EIO;
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;
+ SET_ETHTOOL_OPS(dev, &typhoon_ethtool_ops);
/* We can handle scatter gather, up to 16 entries, and
* we can do IP checksumming (only version 4, doh...)
pci_set_drvdata(pdev, dev);
- printk(KERN_INFO "%s: %s at 0x%lx, ",
- dev->name, typhoon_card_info[card_id].name, ioaddr);
+ printk(KERN_INFO "%s: %s at %s 0x%lx, ",
+ dev->name, typhoon_card_info[card_id].name,
+ use_mmio ? "MMIO" : "IO", pci_resource_start(pdev, use_mmio));
for(i = 0; i < 5; i++)
printk("%2.2x:", dev->dev_addr[i]);
printk("%2.2x\n", dev->dev_addr[i]);
u8 *ver_string = (u8 *) &xp_resp[1];
ver_string[25] = 0;
printk(KERN_INFO "%s: Typhoon 1.1+ Sleep Image version "
- "%u.%u.%u.%u %s\n", dev->name, HIPQUAD(sleep_ver),
+ "%02x.%03x.%03x %s\n", dev->name, sleep_ver >> 24,
+ (sleep_ver >> 12) & 0xfff, sleep_ver & 0xfff,
ver_string);
} else {
printk(KERN_WARNING "%s: Unknown Sleep Image version "
return 0;
error_out_reset:
- typhoon_reset(ioaddr_mapped, NoWait);
+ typhoon_reset(ioaddr, NoWait);
error_out_dma:
pci_free_consistent(pdev, sizeof(struct typhoon_shared),
shared, shared_dma);
error_out_remap:
- iounmap(ioaddr_mapped);
+ pci_iounmap(pdev, ioaddr);
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:
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);
- pci_restore_state(pdev, tp->pci_state);
+ pci_set_power_state(pdev, PCI_D0);
+ pci_restore_state(pdev);
typhoon_reset(tp->ioaddr, NoWait);
- iounmap(tp->ioaddr);
+ pci_iounmap(pdev, tp->ioaddr);
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);