Skip to content
Snippets Groups Projects
amd8111e.c 52.2 KiB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
}
/* This function recalculate the interrupt coalescing  mode on every interrupt
Linus Torvalds's avatar
Linus Torvalds committed
according to the datarate and the packet rate.
*/
static int amd8111e_calc_coalesce(struct net_device *dev)
{
	struct amd8111e_priv *lp = netdev_priv(dev);
	struct amd8111e_coalesce_conf * coal_conf = &lp->coal_conf;
	int tx_pkt_rate;
	int rx_pkt_rate;
	int tx_data_rate;
	int rx_data_rate;
	int rx_pkt_size;
	int tx_pkt_size;

	tx_pkt_rate = coal_conf->tx_packets - coal_conf->tx_prev_packets;
	coal_conf->tx_prev_packets =  coal_conf->tx_packets;
Linus Torvalds's avatar
Linus Torvalds committed
	tx_data_rate = coal_conf->tx_bytes - coal_conf->tx_prev_bytes;
	coal_conf->tx_prev_bytes =  coal_conf->tx_bytes;
Linus Torvalds's avatar
Linus Torvalds committed
	rx_pkt_rate = coal_conf->rx_packets - coal_conf->rx_prev_packets;
	coal_conf->rx_prev_packets =  coal_conf->rx_packets;
Linus Torvalds's avatar
Linus Torvalds committed
	rx_data_rate = coal_conf->rx_bytes - coal_conf->rx_prev_bytes;
	coal_conf->rx_prev_bytes =  coal_conf->rx_bytes;
Linus Torvalds's avatar
Linus Torvalds committed
	if(rx_pkt_rate < 800){
		if(coal_conf->rx_coal_type != NO_COALESCE){
Linus Torvalds's avatar
Linus Torvalds committed
			coal_conf->rx_timeout = 0x0;
			coal_conf->rx_event_count = 0;
			amd8111e_set_coalesce(dev,RX_INTR_COAL);
			coal_conf->rx_coal_type = NO_COALESCE;
		}
	}
	else{
Linus Torvalds's avatar
Linus Torvalds committed
		rx_pkt_size = rx_data_rate/rx_pkt_rate;
		if (rx_pkt_size < 128){
			if(coal_conf->rx_coal_type != NO_COALESCE){
Linus Torvalds's avatar
Linus Torvalds committed
				coal_conf->rx_timeout = 0;
				coal_conf->rx_event_count = 0;
				amd8111e_set_coalesce(dev,RX_INTR_COAL);
				coal_conf->rx_coal_type = NO_COALESCE;
			}

		}
		else if ( (rx_pkt_size >= 128) && (rx_pkt_size < 512) ){
Linus Torvalds's avatar
Linus Torvalds committed
			if(coal_conf->rx_coal_type !=  LOW_COALESCE){
				coal_conf->rx_timeout = 1;
				coal_conf->rx_event_count = 4;
				amd8111e_set_coalesce(dev,RX_INTR_COAL);
				coal_conf->rx_coal_type = LOW_COALESCE;
			}
		}
		else if ((rx_pkt_size >= 512) && (rx_pkt_size < 1024)){
Linus Torvalds's avatar
Linus Torvalds committed
			if(coal_conf->rx_coal_type !=  MEDIUM_COALESCE){
				coal_conf->rx_timeout = 1;
				coal_conf->rx_event_count = 4;
				amd8111e_set_coalesce(dev,RX_INTR_COAL);
				coal_conf->rx_coal_type = MEDIUM_COALESCE;
Linus Torvalds's avatar
Linus Torvalds committed
		}
		else if(rx_pkt_size >= 1024){
			if(coal_conf->rx_coal_type !=  HIGH_COALESCE){
				coal_conf->rx_timeout = 2;
				coal_conf->rx_event_count = 3;
				amd8111e_set_coalesce(dev,RX_INTR_COAL);
				coal_conf->rx_coal_type = HIGH_COALESCE;
Linus Torvalds's avatar
Linus Torvalds committed
		}
	}
    	/* NOW FOR TX INTR COALESC */
	if(tx_pkt_rate < 800){
		if(coal_conf->tx_coal_type != NO_COALESCE){
Linus Torvalds's avatar
Linus Torvalds committed
			coal_conf->tx_timeout = 0x0;
			coal_conf->tx_event_count = 0;
			amd8111e_set_coalesce(dev,TX_INTR_COAL);
			coal_conf->tx_coal_type = NO_COALESCE;
		}
	}
	else{
Linus Torvalds's avatar
Linus Torvalds committed
		tx_pkt_size = tx_data_rate/tx_pkt_rate;
		if (tx_pkt_size < 128){
Linus Torvalds's avatar
Linus Torvalds committed
			if(coal_conf->tx_coal_type != NO_COALESCE){
Linus Torvalds's avatar
Linus Torvalds committed
				coal_conf->tx_timeout = 0;
				coal_conf->tx_event_count = 0;
				amd8111e_set_coalesce(dev,TX_INTR_COAL);
				coal_conf->tx_coal_type = NO_COALESCE;
			}

		}
		else if ( (tx_pkt_size >= 128) && (tx_pkt_size < 512) ){
Linus Torvalds's avatar
Linus Torvalds committed
			if(coal_conf->tx_coal_type !=  LOW_COALESCE){
				coal_conf->tx_timeout = 1;
				coal_conf->tx_event_count = 2;
				amd8111e_set_coalesce(dev,TX_INTR_COAL);
				coal_conf->tx_coal_type = LOW_COALESCE;

			}
		}
		else if ((tx_pkt_size >= 512) && (tx_pkt_size < 1024)){
Linus Torvalds's avatar
Linus Torvalds committed
			if(coal_conf->tx_coal_type !=  MEDIUM_COALESCE){
				coal_conf->tx_timeout = 2;
				coal_conf->tx_event_count = 5;
				amd8111e_set_coalesce(dev,TX_INTR_COAL);
				coal_conf->tx_coal_type = MEDIUM_COALESCE;
Linus Torvalds's avatar
Linus Torvalds committed
		}
		else if(tx_pkt_size >= 1024){
			if (tx_pkt_size >= 1024){
				if(coal_conf->tx_coal_type !=  HIGH_COALESCE){
					coal_conf->tx_timeout = 4;
					coal_conf->tx_event_count = 8;
					amd8111e_set_coalesce(dev,TX_INTR_COAL);
					coal_conf->tx_coal_type = HIGH_COALESCE;
Linus Torvalds's avatar
Linus Torvalds committed
			}
		}
	}
	return 0;

}
/*
This is device interrupt function. It handles transmit, receive,link change and hardware timer interrupts.
*/
static irqreturn_t amd8111e_interrupt(int irq, void *dev_id)
Linus Torvalds's avatar
Linus Torvalds committed
{

	struct net_device * dev = (struct net_device *) dev_id;
	struct amd8111e_priv *lp = netdev_priv(dev);
	void __iomem *mmio = lp->mmio;
	unsigned int intr0, intren0;
Linus Torvalds's avatar
Linus Torvalds committed
	unsigned int handled = 1;

	if(unlikely(dev == NULL))
Linus Torvalds's avatar
Linus Torvalds committed
		return IRQ_NONE;

Linus Torvalds's avatar
Linus Torvalds committed
	/* disabling interrupt */
	writel(INTREN, mmio + CMD0);

	/* Read interrupt status */
	intr0 = readl(mmio + INT0);
	intren0 = readl(mmio + INTEN0);
Linus Torvalds's avatar
Linus Torvalds committed

	/* Process all the INT event until INTR bit is clear. */

	if (!(intr0 & INTR)){
		handled = 0;
		goto err_no_interrupt;
	}
Linus Torvalds's avatar
Linus Torvalds committed
	/* Current driver processes 4 interrupts : RINT,TINT,LCINT,STINT */
	writel(intr0, mmio + INT0);

	/* Check if Receive Interrupt has occurred. */
	if (intr0 & RINT0) {
		if (napi_schedule_prep(&lp->napi)) {
Linus Torvalds's avatar
Linus Torvalds committed
			/* Disable receive interupts */
			writel(RINTEN0, mmio + INTEN0);
			/* Schedule a polling routine */
			__napi_schedule(&lp->napi);
		} else if (intren0 & RINTEN0) {
			printk("************Driver bug! interrupt while in poll\n");
			/* Fix by disable receive interrupts */
			writel(RINTEN0, mmio + INTEN0);
Linus Torvalds's avatar
Linus Torvalds committed
		}
	}
Linus Torvalds's avatar
Linus Torvalds committed
	/* Check if  Transmit Interrupt has occurred. */
Linus Torvalds's avatar
Linus Torvalds committed
		amd8111e_tx(dev);
Linus Torvalds's avatar
Linus Torvalds committed
	/* Check if  Link Change Interrupt has occurred. */
	if (intr0 & LCINT)
		amd8111e_link_change(dev);

	/* Check if Hardware Timer Interrupt has occurred. */
	if (intr0 & STINT)
		amd8111e_calc_coalesce(dev);

err_no_interrupt:
	writel( VAL0 | INTREN,mmio + CMD0);
Linus Torvalds's avatar
Linus Torvalds committed
	return IRQ_RETVAL(handled);
}

#ifdef CONFIG_NET_POLL_CONTROLLER
static void amd8111e_poll(struct net_device *dev)
Linus Torvalds's avatar
Linus Torvalds committed
	unsigned long flags;
	local_irq_restore(flags);
}
Linus Torvalds's avatar
Linus Torvalds committed
#endif


/*
This function closes the network interface and updates the statistics so that most recent statistics will be available after the interface is down.
*/
static int amd8111e_close(struct net_device * dev)
{
	struct amd8111e_priv *lp = netdev_priv(dev);
	netif_stop_queue(dev);
Linus Torvalds's avatar
Linus Torvalds committed
	spin_lock_irq(&lp->lock);
Linus Torvalds's avatar
Linus Torvalds committed
	amd8111e_disable_interrupt(lp);
	amd8111e_stop_chip(lp);

	/* Free transmit and receive skbs */
	amd8111e_free_skbs(lp->amd8111e_net_dev);
Linus Torvalds's avatar
Linus Torvalds committed
	netif_carrier_off(lp->amd8111e_net_dev);

	/* Delete ipg timer */
	if(lp->options & OPTION_DYN_IPG_ENABLE)
Linus Torvalds's avatar
Linus Torvalds committed
		del_timer_sync(&lp->ipg_data.ipg_timer);

	spin_unlock_irq(&lp->lock);
	free_irq(dev->irq, dev);
	amd8111e_free_ring(lp);
Linus Torvalds's avatar
Linus Torvalds committed
	/* Update the statistics before closing */
	amd8111e_get_stats(dev);
	lp->opened = 0;
	return 0;
}
/* This function opens new interface.It requests irq for the device, initializes the device,buffers and descriptors, and starts the device.
Linus Torvalds's avatar
Linus Torvalds committed
*/
static int amd8111e_open(struct net_device * dev )
{
	struct amd8111e_priv *lp = netdev_priv(dev);

	if(dev->irq ==0 || request_irq(dev->irq, amd8111e_interrupt, IRQF_SHARED,
					 dev->name, dev))
Linus Torvalds's avatar
Linus Torvalds committed
		return -EAGAIN;

Linus Torvalds's avatar
Linus Torvalds committed
	spin_lock_irq(&lp->lock);

	amd8111e_init_hw_default(lp);

	if(amd8111e_restart(dev)){
		spin_unlock_irq(&lp->lock);
Linus Torvalds's avatar
Linus Torvalds committed
		if (dev->irq)
			free_irq(dev->irq, dev);
		return -ENOMEM;
	}
	/* Start ipg timer */
	if(lp->options & OPTION_DYN_IPG_ENABLE){
Linus Torvalds's avatar
Linus Torvalds committed
		add_timer(&lp->ipg_data.ipg_timer);
		printk(KERN_INFO "%s: Dynamic IPG Enabled.\n",dev->name);
	}

	lp->opened = 1;

	spin_unlock_irq(&lp->lock);

	netif_start_queue(dev);

Linus Torvalds's avatar
Linus Torvalds committed
}
Linus Torvalds's avatar
Linus Torvalds committed
This function checks if there is any transmit  descriptors available to queue more packet.
*/
static int amd8111e_tx_queue_avail(struct amd8111e_priv* lp )
Linus Torvalds's avatar
Linus Torvalds committed
	int tx_index = lp->tx_idx & TX_BUFF_MOD_MASK;
	if (lp->tx_skbuff[tx_index])
Linus Torvalds's avatar
Linus Torvalds committed
		return -1;
	else
		return 0;
Linus Torvalds's avatar
Linus Torvalds committed
}
Linus Torvalds's avatar
Linus Torvalds committed
This function will queue the transmit packets to the descriptors and will trigger the send operation. It also initializes the transmit descriptors with buffer physical address, byte count, ownership to hardware etc.
*/

static netdev_tx_t amd8111e_start_xmit(struct sk_buff *skb,
				       struct net_device * dev)
Linus Torvalds's avatar
Linus Torvalds committed
{
	struct amd8111e_priv *lp = netdev_priv(dev);
	int tx_index;
	unsigned long flags;

	spin_lock_irqsave(&lp->lock, flags);

	tx_index = lp->tx_idx & TX_RING_DR_MOD_MASK;

	lp->tx_ring[tx_index].buff_count = cpu_to_le16(skb->len);

	lp->tx_skbuff[tx_index] = skb;
	lp->tx_ring[tx_index].tx_flags = 0;

#if AMD8111E_VLAN_TAG_USED
	if((lp->vlgrp != NULL) && vlan_tx_tag_present(skb)){
		lp->tx_ring[tx_index].tag_ctrl_cmd |=
				cpu_to_le16(TCC_VLAN_INSERT);
		lp->tx_ring[tx_index].tag_ctrl_info =
Linus Torvalds's avatar
Linus Torvalds committed
				cpu_to_le16(vlan_tx_tag_get(skb));

	}
#endif
	lp->tx_dma_addr[tx_index] =
	    pci_map_single(lp->pci_dev, skb->data, skb->len, PCI_DMA_TODEVICE);
	lp->tx_ring[tx_index].buff_phy_addr =
	    cpu_to_le32(lp->tx_dma_addr[tx_index]);
Linus Torvalds's avatar
Linus Torvalds committed

	/*  Set FCS and LTINT bits */
	wmb();
	lp->tx_ring[tx_index].tx_flags |=
	    cpu_to_le16(OWN_BIT | STP_BIT | ENP_BIT|ADD_FCS_BIT|LTINT_BIT);

	lp->tx_idx++;

	/* Trigger an immediate send poll. */
	writel( VAL1 | TDMD0, lp->mmio + CMD0);
	writel( VAL2 | RDMD0,lp->mmio + CMD0);

	if(amd8111e_tx_queue_avail(lp) < 0){
		netif_stop_queue(dev);
	}
	spin_unlock_irqrestore(&lp->lock, flags);
Linus Torvalds's avatar
Linus Torvalds committed
}
/*
This function returns all the memory mapped registers of the device.
*/
static void amd8111e_read_regs(struct amd8111e_priv *lp, u32 *buf)
{
	void __iomem *mmio = lp->mmio;
	/* Read only necessary registers */
	buf[0] = readl(mmio + XMT_RING_BASE_ADDR0);
	buf[1] = readl(mmio + XMT_RING_LEN0);
	buf[2] = readl(mmio + RCV_RING_BASE_ADDR0);
	buf[3] = readl(mmio + RCV_RING_LEN0);
	buf[4] = readl(mmio + CMD0);
	buf[5] = readl(mmio + CMD2);
	buf[6] = readl(mmio + CMD3);
	buf[7] = readl(mmio + CMD7);
	buf[8] = readl(mmio + INT0);
	buf[9] = readl(mmio + INTEN0);
	buf[10] = readl(mmio + LADRF);
	buf[11] = readl(mmio + LADRF+4);
	buf[12] = readl(mmio + STAT0);
}

Linus Torvalds's avatar
Linus Torvalds committed
/*
This function sets promiscuos mode, all-multi mode or the multicast address
Linus Torvalds's avatar
Linus Torvalds committed
list to the device.
*/
static void amd8111e_set_multicast_list(struct net_device *dev)
{
	struct netdev_hw_addr *ha;
Linus Torvalds's avatar
Linus Torvalds committed
	struct amd8111e_priv *lp = netdev_priv(dev);
	u32 mc_filter[2] ;
Linus Torvalds's avatar
Linus Torvalds committed
	if(dev->flags & IFF_PROMISC){
		writel( VAL2 | PROM, lp->mmio + CMD2);
		return;
	}
	else
		writel( PROM, lp->mmio + CMD2);
	if (dev->flags & IFF_ALLMULTI ||
	    netdev_mc_count(dev) > MAX_FILTER_SIZE) {
Linus Torvalds's avatar
Linus Torvalds committed
		/* get all multicast packet */
		mc_filter[1] = mc_filter[0] = 0xffffffff;
		lp->options |= OPTION_MULTICAST_ENABLE;
		amd8111e_writeq(*(u64*)mc_filter,lp->mmio + LADRF);
		return;
	}
	if (netdev_mc_empty(dev)) {
Linus Torvalds's avatar
Linus Torvalds committed
		/* get only own packets */
		mc_filter[1] = mc_filter[0] = 0;
		lp->options &= ~OPTION_MULTICAST_ENABLE;
		amd8111e_writeq(*(u64*)mc_filter,lp->mmio + LADRF);
		/* disable promiscous mode */
		writel(PROM, lp->mmio + CMD2);
		return;
	}
	/* load all the multicast addresses in the logic filter */
	lp->options |= OPTION_MULTICAST_ENABLE;
	mc_filter[1] = mc_filter[0] = 0;
	netdev_for_each_mc_addr(ha, dev) {
		bit_num = (ether_crc_le(ETH_ALEN, ha->addr) >> 26) & 0x3f;
Linus Torvalds's avatar
Linus Torvalds committed
		mc_filter[bit_num >> 5] |= 1 << (bit_num & 31);
Linus Torvalds's avatar
Linus Torvalds committed
	amd8111e_writeq(*(u64*)mc_filter,lp->mmio+ LADRF);

	/* To eliminate PCI posting bug */
	readl(lp->mmio + CMD2);

}

static void amd8111e_get_drvinfo(struct net_device* dev, struct ethtool_drvinfo *info)
{
	struct amd8111e_priv *lp = netdev_priv(dev);
	struct pci_dev *pci_dev = lp->pci_dev;
	strcpy (info->driver, MODULE_NAME);
	strcpy (info->version, MODULE_VERS);
	sprintf(info->fw_version,"%u",chip_version);
	strcpy (info->bus_info, pci_name(pci_dev));
}

static int amd8111e_get_regs_len(struct net_device *dev)
{
	return AMD8111E_REG_DUMP_LEN;
}

static void amd8111e_get_regs(struct net_device *dev, struct ethtool_regs *regs, void *buf)
{
	struct amd8111e_priv *lp = netdev_priv(dev);
	regs->version = 0;
	amd8111e_read_regs(lp, buf);
}

static int amd8111e_get_settings(struct net_device *dev, struct ethtool_cmd *ecmd)
{
	struct amd8111e_priv *lp = netdev_priv(dev);
	spin_lock_irq(&lp->lock);
	mii_ethtool_gset(&lp->mii_if, ecmd);
	spin_unlock_irq(&lp->lock);
	return 0;
}

static int amd8111e_set_settings(struct net_device *dev, struct ethtool_cmd *ecmd)
{
	struct amd8111e_priv *lp = netdev_priv(dev);
	int res;
	spin_lock_irq(&lp->lock);
	res = mii_ethtool_sset(&lp->mii_if, ecmd);
	spin_unlock_irq(&lp->lock);
	return res;
}

static int amd8111e_nway_reset(struct net_device *dev)
{
	struct amd8111e_priv *lp = netdev_priv(dev);
	return mii_nway_restart(&lp->mii_if);
}

static u32 amd8111e_get_link(struct net_device *dev)
{
	struct amd8111e_priv *lp = netdev_priv(dev);
	return mii_link_ok(&lp->mii_if);
}

static void amd8111e_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol_info)
{
	struct amd8111e_priv *lp = netdev_priv(dev);
	wol_info->supported = WAKE_MAGIC|WAKE_PHY;
	if (lp->options & OPTION_WOL_ENABLE)
		wol_info->wolopts = WAKE_MAGIC;
}

static int amd8111e_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol_info)
{
	struct amd8111e_priv *lp = netdev_priv(dev);
	if (wol_info->wolopts & ~(WAKE_MAGIC|WAKE_PHY))
		return -EINVAL;
	spin_lock_irq(&lp->lock);
	if (wol_info->wolopts & WAKE_MAGIC)
		lp->options |=
Linus Torvalds's avatar
Linus Torvalds committed
			(OPTION_WOL_ENABLE | OPTION_WAKE_MAGIC_ENABLE);
	else if(wol_info->wolopts & WAKE_PHY)
		lp->options |=
Linus Torvalds's avatar
Linus Torvalds committed
			(OPTION_WOL_ENABLE | OPTION_WAKE_PHY_ENABLE);
	else
		lp->options &= ~OPTION_WOL_ENABLE;
Linus Torvalds's avatar
Linus Torvalds committed
	spin_unlock_irq(&lp->lock);
	return 0;
}

static const struct ethtool_ops ops = {
Linus Torvalds's avatar
Linus Torvalds committed
	.get_drvinfo = amd8111e_get_drvinfo,
	.get_regs_len = amd8111e_get_regs_len,
	.get_regs = amd8111e_get_regs,
	.get_settings = amd8111e_get_settings,
	.set_settings = amd8111e_set_settings,
	.nway_reset = amd8111e_nway_reset,
	.get_link = amd8111e_get_link,
	.get_wol = amd8111e_get_wol,
	.set_wol = amd8111e_set_wol,
};

/*
This function handles all the  ethtool ioctls. It gives driver info, gets/sets driver speed, gets memory mapped register values, forces auto negotiation, sets/gets WOL options for ethtool application.
Linus Torvalds's avatar
Linus Torvalds committed
*/
Linus Torvalds's avatar
Linus Torvalds committed
static int amd8111e_ioctl(struct net_device * dev , struct ifreq *ifr, int cmd)
{
	struct mii_ioctl_data *data = if_mii(ifr);
	struct amd8111e_priv *lp = netdev_priv(dev);
	int err;
	u32 mii_regval;

	switch(cmd) {
	case SIOCGMIIPHY:
		data->phy_id = lp->ext_phy_addr;

	/* fallthru */
	case SIOCGMIIREG:
Linus Torvalds's avatar
Linus Torvalds committed

		spin_lock_irq(&lp->lock);
		err = amd8111e_read_phy(lp, data->phy_id,
			data->reg_num & PHY_REG_ADDR_MASK, &mii_regval);
		spin_unlock_irq(&lp->lock);

		data->val_out = mii_regval;
		return err;

	case SIOCSMIIREG:

		spin_lock_irq(&lp->lock);
		err = amd8111e_write_phy(lp, data->phy_id,
			data->reg_num & PHY_REG_ADDR_MASK, data->val_in);
		spin_unlock_irq(&lp->lock);

		return err;

	default:
		/* do nothing */
		break;
	}
	return -EOPNOTSUPP;
}
static int amd8111e_set_mac_address(struct net_device *dev, void *p)
{
	struct amd8111e_priv *lp = netdev_priv(dev);
	int i;
	struct sockaddr *addr = p;

	memcpy(dev->dev_addr, addr->sa_data, dev->addr_len);
	spin_lock_irq(&lp->lock);
	/* Setting the MAC address to the device */
	for(i = 0; i < ETH_ADDR_LEN; i++)
		writeb( dev->dev_addr[i], lp->mmio + PADR + i );

Linus Torvalds's avatar
Linus Torvalds committed
	spin_unlock_irq(&lp->lock);

	return 0;
}

Linus Torvalds's avatar
Linus Torvalds committed
This function changes the mtu of the device. It restarts the device  to initialize the descriptor with new receive buffers.
Linus Torvalds's avatar
Linus Torvalds committed
static int amd8111e_change_mtu(struct net_device *dev, int new_mtu)
{
	struct amd8111e_priv *lp = netdev_priv(dev);
	int err;

	if ((new_mtu < AMD8111E_MIN_MTU) || (new_mtu > AMD8111E_MAX_MTU))
		return -EINVAL;

	if (!netif_running(dev)) {
		/* new_mtu will be used
		   when device starts netxt time */
Linus Torvalds's avatar
Linus Torvalds committed
		dev->mtu = new_mtu;
		return 0;
	}

	spin_lock_irq(&lp->lock);

        /* stop the chip */
	writel(RUN, lp->mmio + CMD0);

	dev->mtu = new_mtu;

	err = amd8111e_restart(dev);
	spin_unlock_irq(&lp->lock);
	if(!err)
		netif_start_queue(dev);
	return err;
}

#if AMD8111E_VLAN_TAG_USED
static void amd8111e_vlan_rx_register(struct net_device *dev, struct vlan_group *grp)
{
	struct  amd8111e_priv *lp = netdev_priv(dev);
	spin_lock_irq(&lp->lock);
	lp->vlgrp = grp;
	spin_unlock_irq(&lp->lock);
}
#endif
Linus Torvalds's avatar
Linus Torvalds committed
static int amd8111e_enable_magicpkt(struct amd8111e_priv* lp)
{
	writel( VAL1|MPPLBA, lp->mmio + CMD3);
	writel( VAL0|MPEN_SW, lp->mmio + CMD7);

	/* To eliminate PCI posting bug */
	readl(lp->mmio + CMD7);
	return 0;
}

static int amd8111e_enable_link_change(struct amd8111e_priv* lp)
{

	/* Adapter is already stoped/suspended/interrupt-disabled */
	writel(VAL0|LCMODE_SW,lp->mmio + CMD7);
Linus Torvalds's avatar
Linus Torvalds committed
	/* To eliminate PCI posting bug */
	readl(lp->mmio + CMD7);
	return 0;
Linus Torvalds's avatar
Linus Torvalds committed

/*
 * This function is called when a packet transmission fails to complete
 * within a reasonable period, on the assumption that an interrupt have
 * failed or the interface is locked up. This function will reinitialize
 * the hardware.
 */
Linus Torvalds's avatar
Linus Torvalds committed
static void amd8111e_tx_timeout(struct net_device *dev)
{
	struct amd8111e_priv* lp = netdev_priv(dev);
	int err;

	printk(KERN_ERR "%s: transmit timed out, resetting\n",
	 					      dev->name);
	spin_lock_irq(&lp->lock);
	err = amd8111e_restart(dev);
	spin_unlock_irq(&lp->lock);
	if(!err)
		netif_wake_queue(dev);
}
static int amd8111e_suspend(struct pci_dev *pci_dev, pm_message_t state)
Linus Torvalds's avatar
Linus Torvalds committed
	struct net_device *dev = pci_get_drvdata(pci_dev);
	struct amd8111e_priv *lp = netdev_priv(dev);
Linus Torvalds's avatar
Linus Torvalds committed
	if (!netif_running(dev))
		return 0;

	/* disable the interrupt */
	spin_lock_irq(&lp->lock);
	amd8111e_disable_interrupt(lp);
	spin_unlock_irq(&lp->lock);

	netif_device_detach(dev);
Linus Torvalds's avatar
Linus Torvalds committed
	/* stop chip */
	spin_lock_irq(&lp->lock);
	if(lp->options & OPTION_DYN_IPG_ENABLE)
Linus Torvalds's avatar
Linus Torvalds committed
		del_timer_sync(&lp->ipg_data.ipg_timer);
	amd8111e_stop_chip(lp);
	spin_unlock_irq(&lp->lock);

	if(lp->options & OPTION_WOL_ENABLE){
		 /* enable wol */
		if(lp->options & OPTION_WAKE_MAGIC_ENABLE)
			amd8111e_enable_magicpkt(lp);
Linus Torvalds's avatar
Linus Torvalds committed
		if(lp->options & OPTION_WAKE_PHY_ENABLE)
			amd8111e_enable_link_change(lp);

Linus Torvalds's avatar
Linus Torvalds committed
		pci_enable_wake(pci_dev, PCI_D3hot, 1);
		pci_enable_wake(pci_dev, PCI_D3cold, 1);

	}
Linus Torvalds's avatar
Linus Torvalds committed
		pci_enable_wake(pci_dev, PCI_D3hot, 0);
		pci_enable_wake(pci_dev, PCI_D3cold, 0);
	}
Linus Torvalds's avatar
Linus Torvalds committed
	pci_save_state(pci_dev);
	pci_set_power_state(pci_dev, PCI_D3hot);

	return 0;
}
static int amd8111e_resume(struct pci_dev *pci_dev)
{
	struct net_device *dev = pci_get_drvdata(pci_dev);
	struct amd8111e_priv *lp = netdev_priv(dev);
Linus Torvalds's avatar
Linus Torvalds committed
	if (!netif_running(dev))
		return 0;

	pci_set_power_state(pci_dev, PCI_D0);
	pci_restore_state(pci_dev);

	pci_enable_wake(pci_dev, PCI_D3hot, 0);
	pci_enable_wake(pci_dev, PCI_D3cold, 0); /* D3 cold */

	netif_device_attach(dev);

	spin_lock_irq(&lp->lock);
	amd8111e_restart(dev);
	/* Restart ipg timer */
	if(lp->options & OPTION_DYN_IPG_ENABLE)
		mod_timer(&lp->ipg_data.ipg_timer,
Linus Torvalds's avatar
Linus Torvalds committed
				jiffies + IPG_CONVERGE_JIFFIES);
	spin_unlock_irq(&lp->lock);

	return 0;
}


static void __devexit amd8111e_remove_one(struct pci_dev *pdev)
{
	struct net_device *dev = pci_get_drvdata(pdev);
	if (dev) {
		unregister_netdev(dev);
		iounmap(((struct amd8111e_priv *)netdev_priv(dev))->mmio);
		free_netdev(dev);
		pci_release_regions(pdev);
		pci_disable_device(pdev);
		pci_set_drvdata(pdev, NULL);
	}
}
static void amd8111e_config_ipg(struct net_device* dev)
{
	struct amd8111e_priv *lp = netdev_priv(dev);
	struct ipg_info* ipg_data = &lp->ipg_data;
	void __iomem *mmio = lp->mmio;
	unsigned int prev_col_cnt = ipg_data->col_cnt;
	unsigned int total_col_cnt;
	unsigned int tmp_ipg;
Linus Torvalds's avatar
Linus Torvalds committed
	if(lp->link_config.duplex == DUPLEX_FULL){
		ipg_data->ipg = DEFAULT_IPG;
		return;
	}

	if(ipg_data->ipg_state == SSTATE){
Linus Torvalds's avatar
Linus Torvalds committed
		if(ipg_data->timer_tick == IPG_STABLE_TIME){
Linus Torvalds's avatar
Linus Torvalds committed
			ipg_data->timer_tick = 0;
			ipg_data->ipg = MIN_IPG - IPG_STEP;
			ipg_data->current_ipg = MIN_IPG;
			ipg_data->diff_col_cnt = 0xFFFFFFFF;
			ipg_data->ipg_state = CSTATE;
		}
		else
			ipg_data->timer_tick++;
	}

	if(ipg_data->ipg_state == CSTATE){
Linus Torvalds's avatar
Linus Torvalds committed
		/* Get the current collision count */

		total_col_cnt = ipg_data->col_cnt =
Linus Torvalds's avatar
Linus Torvalds committed
				amd8111e_read_mib(mmio, xmt_collisions);

		if ((total_col_cnt - prev_col_cnt) <
Linus Torvalds's avatar
Linus Torvalds committed
				(ipg_data->diff_col_cnt)){
Linus Torvalds's avatar
Linus Torvalds committed
			ipg_data->diff_col_cnt =
				total_col_cnt - prev_col_cnt ;

			ipg_data->ipg = ipg_data->current_ipg;
		}

		ipg_data->current_ipg += IPG_STEP;

		if (ipg_data->current_ipg <= MAX_IPG)
			tmp_ipg = ipg_data->current_ipg;
		else{
			tmp_ipg = ipg_data->ipg;
			ipg_data->ipg_state = SSTATE;
		}
		writew((u32)tmp_ipg, mmio + IPG);
		writew((u32)(tmp_ipg - IFS1_DELTA), mmio + IFS1);
Linus Torvalds's avatar
Linus Torvalds committed
	}
	 mod_timer(&lp->ipg_data.ipg_timer, jiffies + IPG_CONVERGE_JIFFIES);
	return;

}

static void __devinit amd8111e_probe_ext_phy(struct net_device* dev)
{
	struct amd8111e_priv *lp = netdev_priv(dev);
	int i;

	for (i = 0x1e; i >= 0; i--) {
		u32 id1, id2;

		if (amd8111e_read_phy(lp, i, MII_PHYSID1, &id1))
			continue;
		if (amd8111e_read_phy(lp, i, MII_PHYSID2, &id2))
			continue;
		lp->ext_phy_id = (id1 << 16) | id2;
		lp->ext_phy_addr = i;
		return;
	}
	lp->ext_phy_id = 0;
	lp->ext_phy_addr = 1;
}

static const struct net_device_ops amd8111e_netdev_ops = {
	.ndo_open		= amd8111e_open,
	.ndo_stop		= amd8111e_close,
	.ndo_start_xmit		= amd8111e_start_xmit,
	.ndo_tx_timeout		= amd8111e_tx_timeout,
	.ndo_get_stats		= amd8111e_get_stats,
	.ndo_set_multicast_list = amd8111e_set_multicast_list,
	.ndo_validate_addr	= eth_validate_addr,
	.ndo_set_mac_address	= amd8111e_set_mac_address,
	.ndo_do_ioctl		= amd8111e_ioctl,
	.ndo_change_mtu		= amd8111e_change_mtu,
#if AMD8111E_VLAN_TAG_USED
	.ndo_vlan_rx_register	= amd8111e_vlan_rx_register,
#endif
#ifdef CONFIG_NET_POLL_CONTROLLER
	.ndo_poll_controller	 = amd8111e_poll,
#endif
};

Linus Torvalds's avatar
Linus Torvalds committed
static int __devinit amd8111e_probe_one(struct pci_dev *pdev,
				  const struct pci_device_id *ent)
{
	int err,i,pm_cap;
	unsigned long reg_addr,reg_len;
	struct amd8111e_priv* lp;
	struct net_device* dev;

	err = pci_enable_device(pdev);
	if(err){
		printk(KERN_ERR "amd8111e: Cannot enable new PCI device, "
Linus Torvalds's avatar
Linus Torvalds committed
			"exiting.\n");
		return err;
	}

	if(!(pci_resource_flags(pdev, 0) & IORESOURCE_MEM)){
		printk(KERN_ERR "amd8111e: Cannot find PCI base address, "
Linus Torvalds's avatar
Linus Torvalds committed
		       "exiting.\n");
		err = -ENODEV;
		goto err_disable_pdev;
	}

	err = pci_request_regions(pdev, MODULE_NAME);
	if(err){
		printk(KERN_ERR "amd8111e: Cannot obtain PCI resources, "
		       "exiting.\n");
		goto err_disable_pdev;
	}

	pci_set_master(pdev);

	/* Find power-management capability. */
	if((pm_cap = pci_find_capability(pdev, PCI_CAP_ID_PM))==0){
		printk(KERN_ERR "amd8111e: No Power Management capability, "
		       "exiting.\n");
		goto err_free_reg;
	}

	/* Initialize DMA */
	if (pci_set_dma_mask(pdev, DMA_BIT_MASK(32)) < 0) {
Linus Torvalds's avatar
Linus Torvalds committed
		printk(KERN_ERR "amd8111e: DMA not supported,"
			"exiting.\n");
Linus Torvalds's avatar
Linus Torvalds committed
	reg_addr = pci_resource_start(pdev, 0);
	reg_len = pci_resource_len(pdev, 0);

	dev = alloc_etherdev(sizeof(struct amd8111e_priv));
	if (!dev) {
		printk(KERN_ERR "amd8111e: Etherdev alloc failed, exiting.\n");
		err = -ENOMEM;
		goto err_free_reg;
	}

	SET_NETDEV_DEV(dev, &pdev->dev);

#if AMD8111E_VLAN_TAG_USED
	dev->features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX ;
Linus Torvalds's avatar
Linus Torvalds committed
	lp = netdev_priv(dev);
	lp->pci_dev = pdev;
	lp->amd8111e_net_dev = dev;
	lp->pm_cap = pm_cap;

	spin_lock_init(&lp->lock);

	lp->mmio = ioremap(reg_addr, reg_len);
Linus Torvalds's avatar
Linus Torvalds committed
		printk(KERN_ERR "amd8111e: Cannot map device registers, "
		       "exiting\n");
		err = -ENOMEM;
		goto err_free_dev;
	}
Linus Torvalds's avatar
Linus Torvalds committed
	/* Initializing MAC address */
	for(i = 0; i < ETH_ADDR_LEN; i++)
		dev->dev_addr[i] = readb(lp->mmio + PADR + i);
Linus Torvalds's avatar
Linus Torvalds committed
	/* Setting user defined parametrs */
	lp->ext_phy_option = speed_duplex[card_idx];
	if(coalesce[card_idx])
		lp->options |= OPTION_INTR_COAL_ENABLE;
Linus Torvalds's avatar
Linus Torvalds committed
	if(dynamic_ipg[card_idx++])
		lp->options |= OPTION_DYN_IPG_ENABLE;
Linus Torvalds's avatar
Linus Torvalds committed

Linus Torvalds's avatar
Linus Torvalds committed
	/* Initialize driver entry points */
	dev->netdev_ops = &amd8111e_netdev_ops;
Linus Torvalds's avatar
Linus Torvalds committed
	SET_ETHTOOL_OPS(dev, &ops);
	dev->irq =pdev->irq;
	dev->watchdog_timeo = AMD8111E_TX_TIMEOUT;
	netif_napi_add(dev, &lp->napi, amd8111e_rx_poll, 32);
Linus Torvalds's avatar
Linus Torvalds committed

#if AMD8111E_VLAN_TAG_USED
	dev->features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX;
Linus Torvalds's avatar
Linus Torvalds committed
	/* Probe the external PHY */
	amd8111e_probe_ext_phy(dev);

	/* setting mii default values */
	lp->mii_if.dev = dev;
	lp->mii_if.mdio_read = amd8111e_mdio_read;
	lp->mii_if.mdio_write = amd8111e_mdio_write;
	lp->mii_if.phy_id = lp->ext_phy_addr;

	/* Set receive buffer length and set jumbo option*/
	amd8111e_set_rx_buff_len(dev);


	err = register_netdev(dev);
	if (err) {
		printk(KERN_ERR "amd8111e: Cannot register net device, "
		       "exiting.\n");
		goto err_iounmap;
	}

	pci_set_drvdata(pdev, dev);
Linus Torvalds's avatar
Linus Torvalds committed
	/* Initialize software ipg timer */
	if(lp->options & OPTION_DYN_IPG_ENABLE){
Linus Torvalds's avatar
Linus Torvalds committed
		init_timer(&lp->ipg_data.ipg_timer);
		lp->ipg_data.ipg_timer.data = (unsigned long) dev;
		lp->ipg_data.ipg_timer.function = (void *)&amd8111e_config_ipg;
		lp->ipg_data.ipg_timer.expires = jiffies +
Linus Torvalds's avatar
Linus Torvalds committed
						 IPG_CONVERGE_JIFFIES;
		lp->ipg_data.ipg = DEFAULT_IPG;
		lp->ipg_data.ipg_state = CSTATE;
	};

	/*  display driver and device information */

    	chip_version = (readl(lp->mmio + CHIPID) & 0xf0000000)>>28;
	printk(KERN_INFO "%s: AMD-8111e Driver Version: %s\n",
	       dev->name,MODULE_VERS);
	printk(KERN_INFO "%s: [ Rev %x ] PCI 10/100BaseT Ethernet %pM\n",
	       dev->name, chip_version, dev->dev_addr);
Linus Torvalds's avatar
Linus Torvalds committed
	if (lp->ext_phy_id)
		printk(KERN_INFO "%s: Found MII PHY ID 0x%08x at address 0x%02x\n",
		       dev->name, lp->ext_phy_id, lp->ext_phy_addr);
	else
		printk(KERN_INFO "%s: Couldn't detect MII PHY, assuming address 0x01\n",
		       dev->name);
    	return 0;
err_iounmap:
	iounmap(lp->mmio);

err_free_dev:
	free_netdev(dev);

err_free_reg:
	pci_release_regions(pdev);

err_disable_pdev:
	pci_disable_device(pdev);
	pci_set_drvdata(pdev, NULL);
	return err;

}

static struct pci_driver amd8111e_driver = {
	.name   	= MODULE_NAME,
	.id_table	= amd8111e_pci_tbl,
	.probe		= amd8111e_probe_one,
	.remove		= __devexit_p(amd8111e_remove_one),
	.suspend	= amd8111e_suspend,
	.resume		= amd8111e_resume
};