static int nv_probe()

in ethernet/nvidia/forcedeth.c [5709:6157]


static int nv_probe(struct pci_dev *pci_dev, const struct pci_device_id *id)
{
	struct net_device *dev;
	struct fe_priv *np;
	unsigned long addr;
	u8 __iomem *base;
	int err, i;
	u32 powerstate, txreg;
	u32 phystate_orig = 0, phystate;
	int phyinitialized = 0;
	static int printed_version;
	u8 mac[ETH_ALEN];

	if (!printed_version++)
		pr_info("Reverse Engineered nForce ethernet driver. Version %s.\n",
			FORCEDETH_VERSION);

	dev = alloc_etherdev(sizeof(struct fe_priv));
	err = -ENOMEM;
	if (!dev)
		goto out;

	np = netdev_priv(dev);
	np->dev = dev;
	np->pci_dev = pci_dev;
	spin_lock_init(&np->lock);
	spin_lock_init(&np->hwstats_lock);
	SET_NETDEV_DEV(dev, &pci_dev->dev);
	u64_stats_init(&np->swstats_rx_syncp);
	u64_stats_init(&np->swstats_tx_syncp);
	np->txrx_stats = alloc_percpu(struct nv_txrx_stats);
	if (!np->txrx_stats) {
		pr_err("np->txrx_stats, alloc memory error.\n");
		err = -ENOMEM;
		goto out_alloc_percpu;
	}

	timer_setup(&np->oom_kick, nv_do_rx_refill, 0);
	timer_setup(&np->nic_poll, nv_do_nic_poll, 0);
	timer_setup(&np->stats_poll, nv_do_stats_poll, TIMER_DEFERRABLE);

	err = pci_enable_device(pci_dev);
	if (err)
		goto out_free;

	pci_set_master(pci_dev);

	err = pci_request_regions(pci_dev, DRV_NAME);
	if (err < 0)
		goto out_disable;

	if (id->driver_data & (DEV_HAS_VLAN|DEV_HAS_MSI_X|DEV_HAS_POWER_CNTRL|DEV_HAS_STATISTICS_V2|DEV_HAS_STATISTICS_V3))
		np->register_size = NV_PCI_REGSZ_VER3;
	else if (id->driver_data & DEV_HAS_STATISTICS_V1)
		np->register_size = NV_PCI_REGSZ_VER2;
	else
		np->register_size = NV_PCI_REGSZ_VER1;

	err = -EINVAL;
	addr = 0;
	for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) {
		if (pci_resource_flags(pci_dev, i) & IORESOURCE_MEM &&
				pci_resource_len(pci_dev, i) >= np->register_size) {
			addr = pci_resource_start(pci_dev, i);
			break;
		}
	}
	if (i == DEVICE_COUNT_RESOURCE) {
		dev_info(&pci_dev->dev, "Couldn't find register window\n");
		goto out_relreg;
	}

	/* copy of driver data */
	np->driver_data = id->driver_data;
	/* copy of device id */
	np->device_id = id->device;

	/* handle different descriptor versions */
	if (id->driver_data & DEV_HAS_HIGH_DMA) {
		/* packet format 3: supports 40-bit addressing */
		np->desc_ver = DESC_VER_3;
		np->txrxctl_bits = NVREG_TXRXCTL_DESC_3;
		if (dma_64bit) {
			if (dma_set_mask_and_coherent(&pci_dev->dev, DMA_BIT_MASK(39)))
				dev_info(&pci_dev->dev,
					 "64-bit DMA failed, using 32-bit addressing\n");
			else
				dev->features |= NETIF_F_HIGHDMA;
		}
	} else if (id->driver_data & DEV_HAS_LARGEDESC) {
		/* packet format 2: supports jumbo frames */
		np->desc_ver = DESC_VER_2;
		np->txrxctl_bits = NVREG_TXRXCTL_DESC_2;
	} else {
		/* original packet format */
		np->desc_ver = DESC_VER_1;
		np->txrxctl_bits = NVREG_TXRXCTL_DESC_1;
	}

	np->pkt_limit = NV_PKTLIMIT_1;
	if (id->driver_data & DEV_HAS_LARGEDESC)
		np->pkt_limit = NV_PKTLIMIT_2;

	if (id->driver_data & DEV_HAS_CHECKSUM) {
		np->txrxctl_bits |= NVREG_TXRXCTL_RXCHECK;
		dev->hw_features |= NETIF_F_IP_CSUM | NETIF_F_SG |
			NETIF_F_TSO | NETIF_F_RXCSUM;
	}

	np->vlanctl_bits = 0;
	if (id->driver_data & DEV_HAS_VLAN) {
		np->vlanctl_bits = NVREG_VLANCONTROL_ENABLE;
		dev->hw_features |= NETIF_F_HW_VLAN_CTAG_RX |
				    NETIF_F_HW_VLAN_CTAG_TX;
	}

	dev->features |= dev->hw_features;

	/* Add loopback capability to the device. */
	dev->hw_features |= NETIF_F_LOOPBACK;

	/* MTU range: 64 - 1500 or 9100 */
	dev->min_mtu = ETH_ZLEN + ETH_FCS_LEN;
	dev->max_mtu = np->pkt_limit;

	np->pause_flags = NV_PAUSEFRAME_RX_CAPABLE | NV_PAUSEFRAME_RX_REQ | NV_PAUSEFRAME_AUTONEG;
	if ((id->driver_data & DEV_HAS_PAUSEFRAME_TX_V1) ||
	    (id->driver_data & DEV_HAS_PAUSEFRAME_TX_V2) ||
	    (id->driver_data & DEV_HAS_PAUSEFRAME_TX_V3)) {
		np->pause_flags |= NV_PAUSEFRAME_TX_CAPABLE | NV_PAUSEFRAME_TX_REQ;
	}

	err = -ENOMEM;
	np->base = ioremap(addr, np->register_size);
	if (!np->base)
		goto out_relreg;

	np->rx_ring_size = RX_RING_DEFAULT;
	np->tx_ring_size = TX_RING_DEFAULT;

	if (!nv_optimized(np)) {
		np->rx_ring.orig = dma_alloc_coherent(&pci_dev->dev,
						      sizeof(struct ring_desc) *
						      (np->rx_ring_size +
						      np->tx_ring_size),
						      &np->ring_addr,
						      GFP_KERNEL);
		if (!np->rx_ring.orig)
			goto out_unmap;
		np->tx_ring.orig = &np->rx_ring.orig[np->rx_ring_size];
	} else {
		np->rx_ring.ex = dma_alloc_coherent(&pci_dev->dev,
						    sizeof(struct ring_desc_ex) *
						    (np->rx_ring_size +
						    np->tx_ring_size),
						    &np->ring_addr, GFP_KERNEL);
		if (!np->rx_ring.ex)
			goto out_unmap;
		np->tx_ring.ex = &np->rx_ring.ex[np->rx_ring_size];
	}
	np->rx_skb = kcalloc(np->rx_ring_size, sizeof(struct nv_skb_map), GFP_KERNEL);
	np->tx_skb = kcalloc(np->tx_ring_size, sizeof(struct nv_skb_map), GFP_KERNEL);
	if (!np->rx_skb || !np->tx_skb)
		goto out_freering;

	if (!nv_optimized(np))
		dev->netdev_ops = &nv_netdev_ops;
	else
		dev->netdev_ops = &nv_netdev_ops_optimized;

	netif_napi_add(dev, &np->napi, nv_napi_poll, RX_WORK_PER_LOOP);
	dev->ethtool_ops = &ops;
	dev->watchdog_timeo = NV_WATCHDOG_TIMEO;

	pci_set_drvdata(pci_dev, dev);

	/* read the mac address */
	base = get_hwbase(dev);
	np->orig_mac[0] = readl(base + NvRegMacAddrA);
	np->orig_mac[1] = readl(base + NvRegMacAddrB);

	/* check the workaround bit for correct mac address order */
	txreg = readl(base + NvRegTransmitPoll);
	if (id->driver_data & DEV_HAS_CORRECT_MACADDR) {
		/* mac address is already in correct order */
		mac[0] = (np->orig_mac[0] >>  0) & 0xff;
		mac[1] = (np->orig_mac[0] >>  8) & 0xff;
		mac[2] = (np->orig_mac[0] >> 16) & 0xff;
		mac[3] = (np->orig_mac[0] >> 24) & 0xff;
		mac[4] = (np->orig_mac[1] >>  0) & 0xff;
		mac[5] = (np->orig_mac[1] >>  8) & 0xff;
	} else if (txreg & NVREG_TRANSMITPOLL_MAC_ADDR_REV) {
		/* mac address is already in correct order */
		mac[0] = (np->orig_mac[0] >>  0) & 0xff;
		mac[1] = (np->orig_mac[0] >>  8) & 0xff;
		mac[2] = (np->orig_mac[0] >> 16) & 0xff;
		mac[3] = (np->orig_mac[0] >> 24) & 0xff;
		mac[4] = (np->orig_mac[1] >>  0) & 0xff;
		mac[5] = (np->orig_mac[1] >>  8) & 0xff;
		/*
		 * Set orig mac address back to the reversed version.
		 * This flag will be cleared during low power transition.
		 * Therefore, we should always put back the reversed address.
		 */
		np->orig_mac[0] = (mac[5] << 0) + (mac[4] << 8) +
			(mac[3] << 16) + (mac[2] << 24);
		np->orig_mac[1] = (mac[1] << 0) + (mac[0] << 8);
	} else {
		/* need to reverse mac address to correct order */
		mac[0] = (np->orig_mac[1] >>  8) & 0xff;
		mac[1] = (np->orig_mac[1] >>  0) & 0xff;
		mac[2] = (np->orig_mac[0] >> 24) & 0xff;
		mac[3] = (np->orig_mac[0] >> 16) & 0xff;
		mac[4] = (np->orig_mac[0] >>  8) & 0xff;
		mac[5] = (np->orig_mac[0] >>  0) & 0xff;
		writel(txreg|NVREG_TRANSMITPOLL_MAC_ADDR_REV, base + NvRegTransmitPoll);
		dev_dbg(&pci_dev->dev,
			"%s: set workaround bit for reversed mac addr\n",
			__func__);
	}

	if (is_valid_ether_addr(mac)) {
		eth_hw_addr_set(dev, mac);
	} else {
		/*
		 * Bad mac address. At least one bios sets the mac address
		 * to 01:23:45:67:89:ab
		 */
		dev_err(&pci_dev->dev,
			"Invalid MAC address detected: %pM - Please complain to your hardware vendor.\n",
			mac);
		eth_hw_addr_random(dev);
		dev_err(&pci_dev->dev,
			"Using random MAC address: %pM\n", dev->dev_addr);
	}

	/* set mac address */
	nv_copy_mac_to_hw(dev);

	/* disable WOL */
	writel(0, base + NvRegWakeUpFlags);
	np->wolenabled = 0;
	device_set_wakeup_enable(&pci_dev->dev, false);

	if (id->driver_data & DEV_HAS_POWER_CNTRL) {

		/* take phy and nic out of low power mode */
		powerstate = readl(base + NvRegPowerState2);
		powerstate &= ~NVREG_POWERSTATE2_POWERUP_MASK;
		if ((id->driver_data & DEV_NEED_LOW_POWER_FIX) &&
		    pci_dev->revision >= 0xA3)
			powerstate |= NVREG_POWERSTATE2_POWERUP_REV_A3;
		writel(powerstate, base + NvRegPowerState2);
	}

	if (np->desc_ver == DESC_VER_1)
		np->tx_flags = NV_TX_VALID;
	else
		np->tx_flags = NV_TX2_VALID;

	np->msi_flags = 0;
	if ((id->driver_data & DEV_HAS_MSI) && msi)
		np->msi_flags |= NV_MSI_CAPABLE;

	if ((id->driver_data & DEV_HAS_MSI_X) && msix) {
		/* msix has had reported issues when modifying irqmask
		   as in the case of napi, therefore, disable for now
		*/
#if 0
		np->msi_flags |= NV_MSI_X_CAPABLE;
#endif
	}

	if (optimization_mode == NV_OPTIMIZATION_MODE_CPU) {
		np->irqmask = NVREG_IRQMASK_CPU;
		if (np->msi_flags & NV_MSI_X_CAPABLE) /* set number of vectors */
			np->msi_flags |= 0x0001;
	} else if (optimization_mode == NV_OPTIMIZATION_MODE_DYNAMIC &&
		   !(id->driver_data & DEV_NEED_TIMERIRQ)) {
		/* start off in throughput mode */
		np->irqmask = NVREG_IRQMASK_THROUGHPUT;
		/* remove support for msix mode */
		np->msi_flags &= ~NV_MSI_X_CAPABLE;
	} else {
		optimization_mode = NV_OPTIMIZATION_MODE_THROUGHPUT;
		np->irqmask = NVREG_IRQMASK_THROUGHPUT;
		if (np->msi_flags & NV_MSI_X_CAPABLE) /* set number of vectors */
			np->msi_flags |= 0x0003;
	}

	if (id->driver_data & DEV_NEED_TIMERIRQ)
		np->irqmask |= NVREG_IRQ_TIMER;
	if (id->driver_data & DEV_NEED_LINKTIMER) {
		np->need_linktimer = 1;
		np->link_timeout = jiffies + LINK_TIMEOUT;
	} else {
		np->need_linktimer = 0;
	}

	/* Limit the number of tx's outstanding for hw bug */
	if (id->driver_data & DEV_NEED_TX_LIMIT) {
		np->tx_limit = 1;
		if (((id->driver_data & DEV_NEED_TX_LIMIT2) == DEV_NEED_TX_LIMIT2) &&
		    pci_dev->revision >= 0xA2)
			np->tx_limit = 0;
	}

	/* clear phy state and temporarily halt phy interrupts */
	writel(0, base + NvRegMIIMask);
	phystate = readl(base + NvRegAdapterControl);
	if (phystate & NVREG_ADAPTCTL_RUNNING) {
		phystate_orig = 1;
		phystate &= ~NVREG_ADAPTCTL_RUNNING;
		writel(phystate, base + NvRegAdapterControl);
	}
	writel(NVREG_MIISTAT_MASK_ALL, base + NvRegMIIStatus);

	if (id->driver_data & DEV_HAS_MGMT_UNIT) {
		/* management unit running on the mac? */
		if ((readl(base + NvRegTransmitterControl) & NVREG_XMITCTL_MGMT_ST) &&
		    (readl(base + NvRegTransmitterControl) & NVREG_XMITCTL_SYNC_PHY_INIT) &&
		    nv_mgmt_acquire_sema(dev) &&
		    nv_mgmt_get_version(dev)) {
			np->mac_in_use = 1;
			if (np->mgmt_version > 0)
				np->mac_in_use = readl(base + NvRegMgmtUnitControl) & NVREG_MGMTUNITCONTROL_INUSE;
			/* management unit setup the phy already? */
			if (np->mac_in_use &&
			    ((readl(base + NvRegTransmitterControl) & NVREG_XMITCTL_SYNC_MASK) ==
			     NVREG_XMITCTL_SYNC_PHY_INIT)) {
				/* phy is inited by mgmt unit */
				phyinitialized = 1;
			} else {
				/* we need to init the phy */
			}
		}
	}

	/* find a suitable phy */
	for (i = 1; i <= 32; i++) {
		int id1, id2;
		int phyaddr = i & 0x1F;

		spin_lock_irq(&np->lock);
		id1 = mii_rw(dev, phyaddr, MII_PHYSID1, MII_READ);
		spin_unlock_irq(&np->lock);
		if (id1 < 0 || id1 == 0xffff)
			continue;
		spin_lock_irq(&np->lock);
		id2 = mii_rw(dev, phyaddr, MII_PHYSID2, MII_READ);
		spin_unlock_irq(&np->lock);
		if (id2 < 0 || id2 == 0xffff)
			continue;

		np->phy_model = id2 & PHYID2_MODEL_MASK;
		id1 = (id1 & PHYID1_OUI_MASK) << PHYID1_OUI_SHFT;
		id2 = (id2 & PHYID2_OUI_MASK) >> PHYID2_OUI_SHFT;
		np->phyaddr = phyaddr;
		np->phy_oui = id1 | id2;

		/* Realtek hardcoded phy id1 to all zero's on certain phys */
		if (np->phy_oui == PHY_OUI_REALTEK2)
			np->phy_oui = PHY_OUI_REALTEK;
		/* Setup phy revision for Realtek */
		if (np->phy_oui == PHY_OUI_REALTEK && np->phy_model == PHY_MODEL_REALTEK_8211)
			np->phy_rev = mii_rw(dev, phyaddr, MII_RESV1, MII_READ) & PHY_REV_MASK;

		break;
	}
	if (i == 33) {
		dev_info(&pci_dev->dev, "open: Could not find a valid PHY\n");
		goto out_error;
	}

	if (!phyinitialized) {
		/* reset it */
		phy_init(dev);
	} else {
		/* see if it is a gigabit phy */
		u32 mii_status = mii_rw(dev, np->phyaddr, MII_BMSR, MII_READ);
		if (mii_status & PHY_GIGABIT)
			np->gigabit = PHY_GIGABIT;
	}

	/* set default link speed settings */
	np->linkspeed = NVREG_LINKSPEED_FORCE|NVREG_LINKSPEED_10;
	np->duplex = 0;
	np->autoneg = 1;

	err = register_netdev(dev);
	if (err) {
		dev_info(&pci_dev->dev, "unable to register netdev: %d\n", err);
		goto out_error;
	}

	netif_carrier_off(dev);

	/* Some NICs freeze when TX pause is enabled while NIC is
	 * down, and this stays across warm reboots. The sequence
	 * below should be enough to recover from that state.
	 */
	nv_update_pause(dev, 0);
	nv_start_tx(dev);
	nv_stop_tx(dev);

	if (id->driver_data & DEV_HAS_VLAN)
		nv_vlan_mode(dev, dev->features);

	dev_info(&pci_dev->dev, "ifname %s, PHY OUI 0x%x @ %d, addr %pM\n",
		 dev->name, np->phy_oui, np->phyaddr, dev->dev_addr);

	dev_info(&pci_dev->dev, "%s%s%s%s%s%s%s%s%s%s%sdesc-v%u\n",
		 dev->features & NETIF_F_HIGHDMA ? "highdma " : "",
		 dev->features & (NETIF_F_IP_CSUM | NETIF_F_SG) ?
			"csum " : "",
		 dev->features & (NETIF_F_HW_VLAN_CTAG_RX |
				  NETIF_F_HW_VLAN_CTAG_TX) ?
			"vlan " : "",
		 dev->features & (NETIF_F_LOOPBACK) ?
			"loopback " : "",
		 id->driver_data & DEV_HAS_POWER_CNTRL ? "pwrctl " : "",
		 id->driver_data & DEV_HAS_MGMT_UNIT ? "mgmt " : "",
		 id->driver_data & DEV_NEED_TIMERIRQ ? "timirq " : "",
		 np->gigabit == PHY_GIGABIT ? "gbit " : "",
		 np->need_linktimer ? "lnktim " : "",
		 np->msi_flags & NV_MSI_CAPABLE ? "msi " : "",
		 np->msi_flags & NV_MSI_X_CAPABLE ? "msi-x " : "",
		 np->desc_ver);

	return 0;

out_error:
	if (phystate_orig)
		writel(phystate|NVREG_ADAPTCTL_RUNNING, base + NvRegAdapterControl);
out_freering:
	free_rings(dev);
out_unmap:
	iounmap(get_hwbase(dev));
out_relreg:
	pci_release_regions(pci_dev);
out_disable:
	pci_disable_device(pci_dev);
out_free:
	free_percpu(np->txrx_stats);
out_alloc_percpu:
	free_netdev(dev);
out:
	return err;
}