Skip to content
Snippets Groups Projects
3c59x.c 111 KiB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed

	spin_lock_irqsave(&vp->lock, flags);
	EL3WINDOW(4);
	rc = mii_ethtool_gset(&vp->mii, cmd);
	spin_unlock_irqrestore(&vp->lock, flags);
	return rc;
}

static int vortex_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
{
	struct vortex_private *vp = netdev_priv(dev);
	void __iomem *ioaddr = vp->ioaddr;
Linus Torvalds's avatar
Linus Torvalds committed
	unsigned long flags;
	int rc;

	spin_lock_irqsave(&vp->lock, flags);
	EL3WINDOW(4);
	rc = mii_ethtool_sset(&vp->mii, cmd);
	spin_unlock_irqrestore(&vp->lock, flags);
	return rc;
}

static u32 vortex_get_msglevel(struct net_device *dev)
{
	return vortex_debug;
}

static void vortex_set_msglevel(struct net_device *dev, u32 dbg)
{
	vortex_debug = dbg;
}

static int vortex_get_stats_count(struct net_device *dev)
{
	return VORTEX_NUM_STATS;
}

static void vortex_get_ethtool_stats(struct net_device *dev,
	struct ethtool_stats *stats, u64 *data)
{
	struct vortex_private *vp = netdev_priv(dev);
	void __iomem *ioaddr = vp->ioaddr;
Linus Torvalds's avatar
Linus Torvalds committed
	unsigned long flags;

	spin_lock_irqsave(&vp->lock, flags);
	update_stats(ioaddr, dev);
Linus Torvalds's avatar
Linus Torvalds committed
	spin_unlock_irqrestore(&vp->lock, flags);

	data[0] = vp->xstats.tx_deferred;
	data[1] = vp->xstats.tx_max_collisions;
	data[2] = vp->xstats.tx_multiple_collisions;
	data[3] = vp->xstats.tx_single_collisions;
	data[4] = vp->xstats.rx_bad_ssd;
Linus Torvalds's avatar
Linus Torvalds committed
}


static void vortex_get_strings(struct net_device *dev, u32 stringset, u8 *data)
{
	switch (stringset) {
	case ETH_SS_STATS:
		memcpy(data, &ethtool_stats_keys, sizeof(ethtool_stats_keys));
		break;
	default:
		WARN_ON(1);
		break;
	}
}

static void vortex_get_drvinfo(struct net_device *dev,
					struct ethtool_drvinfo *info)
{
	struct vortex_private *vp = netdev_priv(dev);

	strcpy(info->driver, DRV_NAME);
	strcpy(info->version, DRV_VERSION);
	if (VORTEX_PCI(vp)) {
		strcpy(info->bus_info, pci_name(VORTEX_PCI(vp)));
	} else {
		if (VORTEX_EISA(vp))
			sprintf(info->bus_info, vp->gendev->bus_id);
		else
			sprintf(info->bus_info, "EISA 0x%lx %d",
					dev->base_addr, dev->irq);
	}
}

static struct ethtool_ops vortex_ethtool_ops = {
	.get_drvinfo		= vortex_get_drvinfo,
	.get_strings            = vortex_get_strings,
	.get_msglevel           = vortex_get_msglevel,
	.set_msglevel           = vortex_set_msglevel,
	.get_ethtool_stats      = vortex_get_ethtool_stats,
	.get_stats_count        = vortex_get_stats_count,
	.get_settings           = vortex_get_settings,
	.set_settings           = vortex_set_settings,
	.get_link               = vortex_get_link,
	.nway_reset             = vortex_nway_reset,
	.get_perm_addr			= ethtool_op_get_perm_addr,
Linus Torvalds's avatar
Linus Torvalds committed
};

#ifdef CONFIG_PCI
/*
 *	Must power the device up to do MDIO operations
 */
static int vortex_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
{
	int err;
	struct vortex_private *vp = netdev_priv(dev);
	void __iomem *ioaddr = vp->ioaddr;
Linus Torvalds's avatar
Linus Torvalds committed
	unsigned long flags;
	int state = 0;

	if(VORTEX_PCI(vp))
		state = VORTEX_PCI(vp)->current_state;

	/* The kernel core really should have pci_get_power_state() */

	if(state != 0)
		pci_set_power_state(VORTEX_PCI(vp), PCI_D0);
	spin_lock_irqsave(&vp->lock, flags);
	EL3WINDOW(4);
	err = generic_mii_ioctl(&vp->mii, if_mii(rq), cmd, NULL);
	spin_unlock_irqrestore(&vp->lock, flags);
	if(state != 0)
		pci_set_power_state(VORTEX_PCI(vp), state);

	return err;
}
#endif


/* Pre-Cyclone chips have no documented multicast filter, so the only
   multicast setting is to receive all multicast frames.  At least
   the chip has a very clean way to set the mode, unlike many others. */
static void set_rx_mode(struct net_device *dev)
{
	struct vortex_private *vp = netdev_priv(dev);
	void __iomem *ioaddr = vp->ioaddr;
Linus Torvalds's avatar
Linus Torvalds committed
	int new_mode;

	if (dev->flags & IFF_PROMISC) {
		if (vortex_debug > 0)
			printk(KERN_NOTICE "%s: Setting promiscuous mode.\n", dev->name);
		new_mode = SetRxFilter|RxStation|RxMulticast|RxBroadcast|RxProm;
	} else	if ((dev->mc_list)  ||  (dev->flags & IFF_ALLMULTI)) {
		new_mode = SetRxFilter|RxStation|RxMulticast|RxBroadcast;
	} else
		new_mode = SetRxFilter | RxStation | RxBroadcast;

	iowrite16(new_mode, ioaddr + EL3_CMD);
Linus Torvalds's avatar
Linus Torvalds committed
}

#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE)
/* Setup the card so that it can receive frames with an 802.1q VLAN tag.
   Note that this must be done after each RxReset due to some backwards
   compatibility logic in the Cyclone and Tornado ASICs */

/* The Ethernet Type used for 802.1q tagged frames */
#define VLAN_ETHER_TYPE 0x8100

static void set_8021q_mode(struct net_device *dev, int enable)
{
	struct vortex_private *vp = netdev_priv(dev);
	void __iomem *ioaddr = vp->ioaddr;
	int old_window = ioread16(ioaddr + EL3_CMD);
Linus Torvalds's avatar
Linus Torvalds committed
	int mac_ctrl;

	if ((vp->drv_flags&IS_CYCLONE) || (vp->drv_flags&IS_TORNADO)) {
		/* cyclone and tornado chipsets can recognize 802.1q
		 * tagged frames and treat them correctly */

		int max_pkt_size = dev->mtu+14;	/* MTU+Ethernet header */
		if (enable)
			max_pkt_size += 4;	/* 802.1Q VLAN tag */

		EL3WINDOW(3);
		iowrite16(max_pkt_size, ioaddr+Wn3_MaxPktSize);
Linus Torvalds's avatar
Linus Torvalds committed

		/* set VlanEtherType to let the hardware checksumming
		   treat tagged frames correctly */
		EL3WINDOW(7);
		iowrite16(VLAN_ETHER_TYPE, ioaddr+Wn7_VlanEtherType);
Linus Torvalds's avatar
Linus Torvalds committed
	} else {
		/* on older cards we have to enable large frames */

		vp->large_frames = dev->mtu > 1500 || enable;

		EL3WINDOW(3);
		mac_ctrl = ioread16(ioaddr+Wn3_MAC_Ctrl);
Linus Torvalds's avatar
Linus Torvalds committed
		if (vp->large_frames)
			mac_ctrl |= 0x40;
		else
			mac_ctrl &= ~0x40;
		iowrite16(mac_ctrl, ioaddr+Wn3_MAC_Ctrl);
Linus Torvalds's avatar
Linus Torvalds committed
	}

	EL3WINDOW(old_window);
}
#else

static void set_8021q_mode(struct net_device *dev, int enable)
{
}


#endif

/* MII transceiver control section.
   Read and write the MII registers using software-generated serial
   MDIO protocol.  See the MII specifications or DP83840A data sheet
   for details. */

/* The maximum data clock rate is 2.5 Mhz.  The minimum timing is usually
   met by back-to-back PCI I/O cycles, but we insert a delay to avoid
   "overclocking" issues. */
#define mdio_delay() ioread32(mdio_addr)
Linus Torvalds's avatar
Linus Torvalds committed

#define MDIO_SHIFT_CLK	0x01
#define MDIO_DIR_WRITE	0x04
#define MDIO_DATA_WRITE0 (0x00 | MDIO_DIR_WRITE)
#define MDIO_DATA_WRITE1 (0x02 | MDIO_DIR_WRITE)
#define MDIO_DATA_READ	0x02
#define MDIO_ENB_IN		0x00

/* Generate the preamble required for initial synchronization and
   a few older transceivers. */
static void mdio_sync(void __iomem *ioaddr, int bits)
Linus Torvalds's avatar
Linus Torvalds committed
{
	void __iomem *mdio_addr = ioaddr + Wn4_PhysicalMgmt;
Linus Torvalds's avatar
Linus Torvalds committed

	/* Establish sync by sending at least 32 logic ones. */
	while (-- bits >= 0) {
		iowrite16(MDIO_DATA_WRITE1, mdio_addr);
Linus Torvalds's avatar
Linus Torvalds committed
		mdio_delay();
		iowrite16(MDIO_DATA_WRITE1 | MDIO_SHIFT_CLK, mdio_addr);
Linus Torvalds's avatar
Linus Torvalds committed
		mdio_delay();
	}
}

static int mdio_read(struct net_device *dev, int phy_id, int location)
{
	int i;
	struct vortex_private *vp = netdev_priv(dev);
	void __iomem *ioaddr = vp->ioaddr;
Linus Torvalds's avatar
Linus Torvalds committed
	int read_cmd = (0xf6 << 10) | (phy_id << 5) | location;
	unsigned int retval = 0;
	void __iomem *mdio_addr = ioaddr + Wn4_PhysicalMgmt;
Linus Torvalds's avatar
Linus Torvalds committed

	if (mii_preamble_required)
		mdio_sync(ioaddr, 32);

	/* Shift the read command bits out. */
	for (i = 14; i >= 0; i--) {
		int dataval = (read_cmd&(1<<i)) ? MDIO_DATA_WRITE1 : MDIO_DATA_WRITE0;
		iowrite16(dataval, mdio_addr);
Linus Torvalds's avatar
Linus Torvalds committed
		mdio_delay();
		iowrite16(dataval | MDIO_SHIFT_CLK, mdio_addr);
Linus Torvalds's avatar
Linus Torvalds committed
		mdio_delay();
	}
	/* Read the two transition, 16 data, and wire-idle bits. */
	for (i = 19; i > 0; i--) {
		iowrite16(MDIO_ENB_IN, mdio_addr);
Linus Torvalds's avatar
Linus Torvalds committed
		mdio_delay();
		retval = (retval << 1) | ((ioread16(mdio_addr) & MDIO_DATA_READ) ? 1 : 0);
		iowrite16(MDIO_ENB_IN | MDIO_SHIFT_CLK, mdio_addr);
Linus Torvalds's avatar
Linus Torvalds committed
		mdio_delay();
	}
	return retval & 0x20000 ? 0xffff : retval>>1 & 0xffff;
}

static void mdio_write(struct net_device *dev, int phy_id, int location, int value)
{
	struct vortex_private *vp = netdev_priv(dev);
	void __iomem *ioaddr = vp->ioaddr;
Linus Torvalds's avatar
Linus Torvalds committed
	int write_cmd = 0x50020000 | (phy_id << 23) | (location << 18) | value;
	void __iomem *mdio_addr = ioaddr + Wn4_PhysicalMgmt;
Linus Torvalds's avatar
Linus Torvalds committed
	int i;

	if (mii_preamble_required)
		mdio_sync(ioaddr, 32);

	/* Shift the command bits out. */
	for (i = 31; i >= 0; i--) {
		int dataval = (write_cmd&(1<<i)) ? MDIO_DATA_WRITE1 : MDIO_DATA_WRITE0;
		iowrite16(dataval, mdio_addr);
Linus Torvalds's avatar
Linus Torvalds committed
		mdio_delay();
		iowrite16(dataval | MDIO_SHIFT_CLK, mdio_addr);
Linus Torvalds's avatar
Linus Torvalds committed
		mdio_delay();
	}
	/* Leave the interface idle. */
	for (i = 1; i >= 0; i--) {
		iowrite16(MDIO_ENB_IN, mdio_addr);
Linus Torvalds's avatar
Linus Torvalds committed
		mdio_delay();
		iowrite16(MDIO_ENB_IN | MDIO_SHIFT_CLK, mdio_addr);
Linus Torvalds's avatar
Linus Torvalds committed
		mdio_delay();
	}
	return;
}

/* ACPI: Advanced Configuration and Power Interface. */
/* Set Wake-On-LAN mode and put the board into D3 (power-down) state. */
static void acpi_set_WOL(struct net_device *dev)
{
	struct vortex_private *vp = netdev_priv(dev);
	void __iomem *ioaddr = vp->ioaddr;
Linus Torvalds's avatar
Linus Torvalds committed

	if (vp->enable_wol) {
		/* Power up on: 1==Downloaded Filter, 2==Magic Packets, 4==Link Status. */
		EL3WINDOW(7);
		iowrite16(2, ioaddr + 0x0c);
Linus Torvalds's avatar
Linus Torvalds committed
		/* The RxFilter must accept the WOL frames. */
		iowrite16(SetRxFilter|RxStation|RxMulticast|RxBroadcast, ioaddr + EL3_CMD);
		iowrite16(RxEnable, ioaddr + EL3_CMD);
Linus Torvalds's avatar
Linus Torvalds committed

		pci_enable_wake(VORTEX_PCI(vp), 0, 1);

		/* Change the power state to D3; RxEnable doesn't take effect. */
		pci_set_power_state(VORTEX_PCI(vp), PCI_D3hot);
Linus Torvalds's avatar
Linus Torvalds committed
	}
}


static void __devexit vortex_remove_one (struct pci_dev *pdev)
{
	struct net_device *dev = pci_get_drvdata(pdev);
	struct vortex_private *vp;

	if (!dev) {
		printk("vortex_remove_one called for Compaq device!\n");
		BUG();
	}

	vp = netdev_priv(dev);

	if (vp->cb_fn_base)
		pci_iounmap(VORTEX_PCI(vp), vp->cb_fn_base);

Linus Torvalds's avatar
Linus Torvalds committed
	unregister_netdev(dev);

	if (VORTEX_PCI(vp)) {
		pci_set_power_state(VORTEX_PCI(vp), PCI_D0);	/* Go active */
		if (vp->pm_state_valid)
			pci_restore_state(VORTEX_PCI(vp));
		pci_disable_device(VORTEX_PCI(vp));
	}
	/* Should really use issue_and_wait() here */
	iowrite16(TotalReset | ((vp->drv_flags & EEPROM_RESET) ? 0x04 : 0x14),
	     vp->ioaddr + EL3_CMD);

	pci_iounmap(VORTEX_PCI(vp), vp->ioaddr);
Linus Torvalds's avatar
Linus Torvalds committed

	pci_free_consistent(pdev,
						sizeof(struct boom_rx_desc) * RX_RING_SIZE
							+ sizeof(struct boom_tx_desc) * TX_RING_SIZE,
						vp->rx_ring,
						vp->rx_ring_dma);
	if (vp->must_free_region)
		release_region(dev->base_addr, vp->io_size);
	free_netdev(dev);
}


static struct pci_driver vortex_driver = {
	.name		= "3c59x",
	.probe		= vortex_init_one,
	.remove		= __devexit_p(vortex_remove_one),
	.id_table	= vortex_pci_tbl,
#ifdef CONFIG_PM
	.suspend	= vortex_suspend,
	.resume		= vortex_resume,
#endif
};


static int vortex_have_pci;
static int vortex_have_eisa;


static int __init vortex_init (void)
{
	int pci_rc, eisa_rc;

	pci_rc = pci_module_init(&vortex_driver);
	eisa_rc = vortex_eisa_init();

	if (pci_rc == 0)
		vortex_have_pci = 1;
	if (eisa_rc > 0)
		vortex_have_eisa = 1;

	return (vortex_have_pci + vortex_have_eisa) ? 0 : -ENODEV;
}


static void __exit vortex_eisa_cleanup (void)
{
	struct vortex_private *vp;
	void __iomem *ioaddr;
Linus Torvalds's avatar
Linus Torvalds committed

#ifdef CONFIG_EISA
	/* Take care of the EISA devices */
	eisa_driver_unregister (&vortex_eisa_driver);
#endif
	
	if (compaq_net_device) {
		vp = compaq_net_device->priv;
		ioaddr = ioport_map(compaq_net_device->base_addr,
		                    VORTEX_TOTAL_SIZE);
Linus Torvalds's avatar
Linus Torvalds committed

		unregister_netdev (compaq_net_device);
		iowrite16 (TotalReset, ioaddr + EL3_CMD);
		release_region(compaq_net_device->base_addr,
		               VORTEX_TOTAL_SIZE);
Linus Torvalds's avatar
Linus Torvalds committed

		free_netdev (compaq_net_device);
	}
}


static void __exit vortex_cleanup (void)
{
	if (vortex_have_pci)
		pci_unregister_driver (&vortex_driver);
	if (vortex_have_eisa)
		vortex_eisa_cleanup ();
}


module_init(vortex_init);
module_exit(vortex_cleanup);


/*
 * Local variables:
 *  c-indent-level: 4
 *  c-basic-offset: 4
 *  tab-width: 4
 * End:
 */