18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright(c) 2008 - 2009 Atheros Corporation. All rights reserved.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Derived from Intel e1000 driver
68c2ecf20Sopenharmony_ci * Copyright(c) 1999 - 2005 Intel Corporation. All rights reserved.
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include "atl1c.h"
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_cichar atl1c_driver_name[] = "atl1c";
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci/*
148c2ecf20Sopenharmony_ci * atl1c_pci_tbl - PCI Device ID Table
158c2ecf20Sopenharmony_ci *
168c2ecf20Sopenharmony_ci * Wildcard entries (PCI_ANY_ID) should come last
178c2ecf20Sopenharmony_ci * Last entry must be all 0s
188c2ecf20Sopenharmony_ci *
198c2ecf20Sopenharmony_ci * { Vendor ID, Device ID, SubVendor ID, SubDevice ID,
208c2ecf20Sopenharmony_ci *   Class, Class Mask, private data (not used) }
218c2ecf20Sopenharmony_ci */
228c2ecf20Sopenharmony_cistatic const struct pci_device_id atl1c_pci_tbl[] = {
238c2ecf20Sopenharmony_ci	{PCI_DEVICE(PCI_VENDOR_ID_ATTANSIC, PCI_DEVICE_ID_ATTANSIC_L1C)},
248c2ecf20Sopenharmony_ci	{PCI_DEVICE(PCI_VENDOR_ID_ATTANSIC, PCI_DEVICE_ID_ATTANSIC_L2C)},
258c2ecf20Sopenharmony_ci	{PCI_DEVICE(PCI_VENDOR_ID_ATTANSIC, PCI_DEVICE_ID_ATHEROS_L2C_B)},
268c2ecf20Sopenharmony_ci	{PCI_DEVICE(PCI_VENDOR_ID_ATTANSIC, PCI_DEVICE_ID_ATHEROS_L2C_B2)},
278c2ecf20Sopenharmony_ci	{PCI_DEVICE(PCI_VENDOR_ID_ATTANSIC, PCI_DEVICE_ID_ATHEROS_L1D)},
288c2ecf20Sopenharmony_ci	{PCI_DEVICE(PCI_VENDOR_ID_ATTANSIC, PCI_DEVICE_ID_ATHEROS_L1D_2_0)},
298c2ecf20Sopenharmony_ci	/* required last entry */
308c2ecf20Sopenharmony_ci	{ 0 }
318c2ecf20Sopenharmony_ci};
328c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, atl1c_pci_tbl);
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ciMODULE_AUTHOR("Jie Yang");
358c2ecf20Sopenharmony_ciMODULE_AUTHOR("Qualcomm Atheros Inc., <nic-devel@qualcomm.com>");
368c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Qualcomm Atheros 100/1000M Ethernet Network Driver");
378c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_cistatic int atl1c_stop_mac(struct atl1c_hw *hw);
408c2ecf20Sopenharmony_cistatic void atl1c_disable_l0s_l1(struct atl1c_hw *hw);
418c2ecf20Sopenharmony_cistatic void atl1c_set_aspm(struct atl1c_hw *hw, u16 link_speed);
428c2ecf20Sopenharmony_cistatic void atl1c_start_mac(struct atl1c_adapter *adapter);
438c2ecf20Sopenharmony_cistatic void atl1c_clean_rx_irq(struct atl1c_adapter *adapter,
448c2ecf20Sopenharmony_ci		   int *work_done, int work_to_do);
458c2ecf20Sopenharmony_cistatic int atl1c_up(struct atl1c_adapter *adapter);
468c2ecf20Sopenharmony_cistatic void atl1c_down(struct atl1c_adapter *adapter);
478c2ecf20Sopenharmony_cistatic int atl1c_reset_mac(struct atl1c_hw *hw);
488c2ecf20Sopenharmony_cistatic void atl1c_reset_dma_ring(struct atl1c_adapter *adapter);
498c2ecf20Sopenharmony_cistatic int atl1c_configure(struct atl1c_adapter *adapter);
508c2ecf20Sopenharmony_cistatic int atl1c_alloc_rx_buffer(struct atl1c_adapter *adapter);
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_cistatic const u32 atl1c_default_msg = NETIF_MSG_DRV | NETIF_MSG_PROBE |
548c2ecf20Sopenharmony_ci	NETIF_MSG_LINK | NETIF_MSG_TIMER | NETIF_MSG_IFDOWN | NETIF_MSG_IFUP;
558c2ecf20Sopenharmony_cistatic void atl1c_pcie_patch(struct atl1c_hw *hw)
568c2ecf20Sopenharmony_ci{
578c2ecf20Sopenharmony_ci	u32 mst_data, data;
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci	/* pclk sel could switch to 25M */
608c2ecf20Sopenharmony_ci	AT_READ_REG(hw, REG_MASTER_CTRL, &mst_data);
618c2ecf20Sopenharmony_ci	mst_data &= ~MASTER_CTRL_CLK_SEL_DIS;
628c2ecf20Sopenharmony_ci	AT_WRITE_REG(hw, REG_MASTER_CTRL, mst_data);
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci	/* WoL/PCIE related settings */
658c2ecf20Sopenharmony_ci	if (hw->nic_type == athr_l1c || hw->nic_type == athr_l2c) {
668c2ecf20Sopenharmony_ci		AT_READ_REG(hw, REG_PCIE_PHYMISC, &data);
678c2ecf20Sopenharmony_ci		data |= PCIE_PHYMISC_FORCE_RCV_DET;
688c2ecf20Sopenharmony_ci		AT_WRITE_REG(hw, REG_PCIE_PHYMISC, data);
698c2ecf20Sopenharmony_ci	} else { /* new dev set bit5 of MASTER */
708c2ecf20Sopenharmony_ci		if (!(mst_data & MASTER_CTRL_WAKEN_25M))
718c2ecf20Sopenharmony_ci			AT_WRITE_REG(hw, REG_MASTER_CTRL,
728c2ecf20Sopenharmony_ci				mst_data | MASTER_CTRL_WAKEN_25M);
738c2ecf20Sopenharmony_ci	}
748c2ecf20Sopenharmony_ci	/* aspm/PCIE setting only for l2cb 1.0 */
758c2ecf20Sopenharmony_ci	if (hw->nic_type == athr_l2c_b && hw->revision_id == L2CB_V10) {
768c2ecf20Sopenharmony_ci		AT_READ_REG(hw, REG_PCIE_PHYMISC2, &data);
778c2ecf20Sopenharmony_ci		data = FIELD_SETX(data, PCIE_PHYMISC2_CDR_BW,
788c2ecf20Sopenharmony_ci			L2CB1_PCIE_PHYMISC2_CDR_BW);
798c2ecf20Sopenharmony_ci		data = FIELD_SETX(data, PCIE_PHYMISC2_L0S_TH,
808c2ecf20Sopenharmony_ci			L2CB1_PCIE_PHYMISC2_L0S_TH);
818c2ecf20Sopenharmony_ci		AT_WRITE_REG(hw, REG_PCIE_PHYMISC2, data);
828c2ecf20Sopenharmony_ci		/* extend L1 sync timer */
838c2ecf20Sopenharmony_ci		AT_READ_REG(hw, REG_LINK_CTRL, &data);
848c2ecf20Sopenharmony_ci		data |= LINK_CTRL_EXT_SYNC;
858c2ecf20Sopenharmony_ci		AT_WRITE_REG(hw, REG_LINK_CTRL, data);
868c2ecf20Sopenharmony_ci	}
878c2ecf20Sopenharmony_ci	/* l2cb 1.x & l1d 1.x */
888c2ecf20Sopenharmony_ci	if (hw->nic_type == athr_l2c_b || hw->nic_type == athr_l1d) {
898c2ecf20Sopenharmony_ci		AT_READ_REG(hw, REG_PM_CTRL, &data);
908c2ecf20Sopenharmony_ci		data |= PM_CTRL_L0S_BUFSRX_EN;
918c2ecf20Sopenharmony_ci		AT_WRITE_REG(hw, REG_PM_CTRL, data);
928c2ecf20Sopenharmony_ci		/* clear vendor msg */
938c2ecf20Sopenharmony_ci		AT_READ_REG(hw, REG_DMA_DBG, &data);
948c2ecf20Sopenharmony_ci		AT_WRITE_REG(hw, REG_DMA_DBG, data & ~DMA_DBG_VENDOR_MSG);
958c2ecf20Sopenharmony_ci	}
968c2ecf20Sopenharmony_ci}
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci/* FIXME: no need any more ? */
998c2ecf20Sopenharmony_ci/*
1008c2ecf20Sopenharmony_ci * atl1c_init_pcie - init PCIE module
1018c2ecf20Sopenharmony_ci */
1028c2ecf20Sopenharmony_cistatic void atl1c_reset_pcie(struct atl1c_hw *hw, u32 flag)
1038c2ecf20Sopenharmony_ci{
1048c2ecf20Sopenharmony_ci	u32 data;
1058c2ecf20Sopenharmony_ci	u32 pci_cmd;
1068c2ecf20Sopenharmony_ci	struct pci_dev *pdev = hw->adapter->pdev;
1078c2ecf20Sopenharmony_ci	int pos;
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	AT_READ_REG(hw, PCI_COMMAND, &pci_cmd);
1108c2ecf20Sopenharmony_ci	pci_cmd &= ~PCI_COMMAND_INTX_DISABLE;
1118c2ecf20Sopenharmony_ci	pci_cmd |= (PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER |
1128c2ecf20Sopenharmony_ci		PCI_COMMAND_IO);
1138c2ecf20Sopenharmony_ci	AT_WRITE_REG(hw, PCI_COMMAND, pci_cmd);
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	/*
1168c2ecf20Sopenharmony_ci	 * Clear any PowerSaveing Settings
1178c2ecf20Sopenharmony_ci	 */
1188c2ecf20Sopenharmony_ci	pci_enable_wake(pdev, PCI_D3hot, 0);
1198c2ecf20Sopenharmony_ci	pci_enable_wake(pdev, PCI_D3cold, 0);
1208c2ecf20Sopenharmony_ci	/* wol sts read-clear */
1218c2ecf20Sopenharmony_ci	AT_READ_REG(hw, REG_WOL_CTRL, &data);
1228c2ecf20Sopenharmony_ci	AT_WRITE_REG(hw, REG_WOL_CTRL, 0);
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	/*
1258c2ecf20Sopenharmony_ci	 * Mask some pcie error bits
1268c2ecf20Sopenharmony_ci	 */
1278c2ecf20Sopenharmony_ci	pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_ERR);
1288c2ecf20Sopenharmony_ci	if (pos) {
1298c2ecf20Sopenharmony_ci		pci_read_config_dword(pdev, pos + PCI_ERR_UNCOR_SEVER, &data);
1308c2ecf20Sopenharmony_ci		data &= ~(PCI_ERR_UNC_DLP | PCI_ERR_UNC_FCP);
1318c2ecf20Sopenharmony_ci		pci_write_config_dword(pdev, pos + PCI_ERR_UNCOR_SEVER, data);
1328c2ecf20Sopenharmony_ci	}
1338c2ecf20Sopenharmony_ci	/* clear error status */
1348c2ecf20Sopenharmony_ci	pcie_capability_write_word(pdev, PCI_EXP_DEVSTA,
1358c2ecf20Sopenharmony_ci			PCI_EXP_DEVSTA_NFED |
1368c2ecf20Sopenharmony_ci			PCI_EXP_DEVSTA_FED |
1378c2ecf20Sopenharmony_ci			PCI_EXP_DEVSTA_CED |
1388c2ecf20Sopenharmony_ci			PCI_EXP_DEVSTA_URD);
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci	AT_READ_REG(hw, REG_LTSSM_ID_CTRL, &data);
1418c2ecf20Sopenharmony_ci	data &= ~LTSSM_ID_EN_WRO;
1428c2ecf20Sopenharmony_ci	AT_WRITE_REG(hw, REG_LTSSM_ID_CTRL, data);
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	atl1c_pcie_patch(hw);
1458c2ecf20Sopenharmony_ci	if (flag & ATL1C_PCIE_L0S_L1_DISABLE)
1468c2ecf20Sopenharmony_ci		atl1c_disable_l0s_l1(hw);
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	msleep(5);
1498c2ecf20Sopenharmony_ci}
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci/**
1528c2ecf20Sopenharmony_ci * atl1c_irq_enable - Enable default interrupt generation settings
1538c2ecf20Sopenharmony_ci * @adapter: board private structure
1548c2ecf20Sopenharmony_ci */
1558c2ecf20Sopenharmony_cistatic inline void atl1c_irq_enable(struct atl1c_adapter *adapter)
1568c2ecf20Sopenharmony_ci{
1578c2ecf20Sopenharmony_ci	if (likely(atomic_dec_and_test(&adapter->irq_sem))) {
1588c2ecf20Sopenharmony_ci		AT_WRITE_REG(&adapter->hw, REG_ISR, 0x7FFFFFFF);
1598c2ecf20Sopenharmony_ci		AT_WRITE_REG(&adapter->hw, REG_IMR, adapter->hw.intr_mask);
1608c2ecf20Sopenharmony_ci		AT_WRITE_FLUSH(&adapter->hw);
1618c2ecf20Sopenharmony_ci	}
1628c2ecf20Sopenharmony_ci}
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci/**
1658c2ecf20Sopenharmony_ci * atl1c_irq_disable - Mask off interrupt generation on the NIC
1668c2ecf20Sopenharmony_ci * @adapter: board private structure
1678c2ecf20Sopenharmony_ci */
1688c2ecf20Sopenharmony_cistatic inline void atl1c_irq_disable(struct atl1c_adapter *adapter)
1698c2ecf20Sopenharmony_ci{
1708c2ecf20Sopenharmony_ci	atomic_inc(&adapter->irq_sem);
1718c2ecf20Sopenharmony_ci	AT_WRITE_REG(&adapter->hw, REG_IMR, 0);
1728c2ecf20Sopenharmony_ci	AT_WRITE_REG(&adapter->hw, REG_ISR, ISR_DIS_INT);
1738c2ecf20Sopenharmony_ci	AT_WRITE_FLUSH(&adapter->hw);
1748c2ecf20Sopenharmony_ci	synchronize_irq(adapter->pdev->irq);
1758c2ecf20Sopenharmony_ci}
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci/**
1788c2ecf20Sopenharmony_ci * atl1c_irq_reset - reset interrupt confiure on the NIC
1798c2ecf20Sopenharmony_ci * @adapter: board private structure
1808c2ecf20Sopenharmony_ci */
1818c2ecf20Sopenharmony_cistatic inline void atl1c_irq_reset(struct atl1c_adapter *adapter)
1828c2ecf20Sopenharmony_ci{
1838c2ecf20Sopenharmony_ci	atomic_set(&adapter->irq_sem, 1);
1848c2ecf20Sopenharmony_ci	atl1c_irq_enable(adapter);
1858c2ecf20Sopenharmony_ci}
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci/*
1888c2ecf20Sopenharmony_ci * atl1c_wait_until_idle - wait up to AT_HW_MAX_IDLE_DELAY reads
1898c2ecf20Sopenharmony_ci * of the idle status register until the device is actually idle
1908c2ecf20Sopenharmony_ci */
1918c2ecf20Sopenharmony_cistatic u32 atl1c_wait_until_idle(struct atl1c_hw *hw, u32 modu_ctrl)
1928c2ecf20Sopenharmony_ci{
1938c2ecf20Sopenharmony_ci	int timeout;
1948c2ecf20Sopenharmony_ci	u32 data;
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci	for (timeout = 0; timeout < AT_HW_MAX_IDLE_DELAY; timeout++) {
1978c2ecf20Sopenharmony_ci		AT_READ_REG(hw, REG_IDLE_STATUS, &data);
1988c2ecf20Sopenharmony_ci		if ((data & modu_ctrl) == 0)
1998c2ecf20Sopenharmony_ci			return 0;
2008c2ecf20Sopenharmony_ci		msleep(1);
2018c2ecf20Sopenharmony_ci	}
2028c2ecf20Sopenharmony_ci	return data;
2038c2ecf20Sopenharmony_ci}
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci/**
2068c2ecf20Sopenharmony_ci * atl1c_phy_config - Timer Call-back
2078c2ecf20Sopenharmony_ci * @t: timer list containing pointer to netdev cast into an unsigned long
2088c2ecf20Sopenharmony_ci */
2098c2ecf20Sopenharmony_cistatic void atl1c_phy_config(struct timer_list *t)
2108c2ecf20Sopenharmony_ci{
2118c2ecf20Sopenharmony_ci	struct atl1c_adapter *adapter = from_timer(adapter, t,
2128c2ecf20Sopenharmony_ci						   phy_config_timer);
2138c2ecf20Sopenharmony_ci	struct atl1c_hw *hw = &adapter->hw;
2148c2ecf20Sopenharmony_ci	unsigned long flags;
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	spin_lock_irqsave(&adapter->mdio_lock, flags);
2178c2ecf20Sopenharmony_ci	atl1c_restart_autoneg(hw);
2188c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&adapter->mdio_lock, flags);
2198c2ecf20Sopenharmony_ci}
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_civoid atl1c_reinit_locked(struct atl1c_adapter *adapter)
2228c2ecf20Sopenharmony_ci{
2238c2ecf20Sopenharmony_ci	atl1c_down(adapter);
2248c2ecf20Sopenharmony_ci	atl1c_up(adapter);
2258c2ecf20Sopenharmony_ci	clear_bit(__AT_RESETTING, &adapter->flags);
2268c2ecf20Sopenharmony_ci}
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_cistatic void atl1c_check_link_status(struct atl1c_adapter *adapter)
2298c2ecf20Sopenharmony_ci{
2308c2ecf20Sopenharmony_ci	struct atl1c_hw *hw = &adapter->hw;
2318c2ecf20Sopenharmony_ci	struct net_device *netdev = adapter->netdev;
2328c2ecf20Sopenharmony_ci	struct pci_dev    *pdev   = adapter->pdev;
2338c2ecf20Sopenharmony_ci	int err;
2348c2ecf20Sopenharmony_ci	unsigned long flags;
2358c2ecf20Sopenharmony_ci	u16 speed, duplex, phy_data;
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	spin_lock_irqsave(&adapter->mdio_lock, flags);
2388c2ecf20Sopenharmony_ci	/* MII_BMSR must read twise */
2398c2ecf20Sopenharmony_ci	atl1c_read_phy_reg(hw, MII_BMSR, &phy_data);
2408c2ecf20Sopenharmony_ci	atl1c_read_phy_reg(hw, MII_BMSR, &phy_data);
2418c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&adapter->mdio_lock, flags);
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	if ((phy_data & BMSR_LSTATUS) == 0) {
2448c2ecf20Sopenharmony_ci		/* link down */
2458c2ecf20Sopenharmony_ci		netif_carrier_off(netdev);
2468c2ecf20Sopenharmony_ci		hw->hibernate = true;
2478c2ecf20Sopenharmony_ci		if (atl1c_reset_mac(hw) != 0)
2488c2ecf20Sopenharmony_ci			if (netif_msg_hw(adapter))
2498c2ecf20Sopenharmony_ci				dev_warn(&pdev->dev, "reset mac failed\n");
2508c2ecf20Sopenharmony_ci		atl1c_set_aspm(hw, SPEED_0);
2518c2ecf20Sopenharmony_ci		atl1c_post_phy_linkchg(hw, SPEED_0);
2528c2ecf20Sopenharmony_ci		atl1c_reset_dma_ring(adapter);
2538c2ecf20Sopenharmony_ci		atl1c_configure(adapter);
2548c2ecf20Sopenharmony_ci	} else {
2558c2ecf20Sopenharmony_ci		/* Link Up */
2568c2ecf20Sopenharmony_ci		hw->hibernate = false;
2578c2ecf20Sopenharmony_ci		spin_lock_irqsave(&adapter->mdio_lock, flags);
2588c2ecf20Sopenharmony_ci		err = atl1c_get_speed_and_duplex(hw, &speed, &duplex);
2598c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&adapter->mdio_lock, flags);
2608c2ecf20Sopenharmony_ci		if (unlikely(err))
2618c2ecf20Sopenharmony_ci			return;
2628c2ecf20Sopenharmony_ci		/* link result is our setting */
2638c2ecf20Sopenharmony_ci		if (adapter->link_speed != speed ||
2648c2ecf20Sopenharmony_ci		    adapter->link_duplex != duplex) {
2658c2ecf20Sopenharmony_ci			adapter->link_speed  = speed;
2668c2ecf20Sopenharmony_ci			adapter->link_duplex = duplex;
2678c2ecf20Sopenharmony_ci			atl1c_set_aspm(hw, speed);
2688c2ecf20Sopenharmony_ci			atl1c_post_phy_linkchg(hw, speed);
2698c2ecf20Sopenharmony_ci			atl1c_start_mac(adapter);
2708c2ecf20Sopenharmony_ci			if (netif_msg_link(adapter))
2718c2ecf20Sopenharmony_ci				dev_info(&pdev->dev,
2728c2ecf20Sopenharmony_ci					"%s: %s NIC Link is Up<%d Mbps %s>\n",
2738c2ecf20Sopenharmony_ci					atl1c_driver_name, netdev->name,
2748c2ecf20Sopenharmony_ci					adapter->link_speed,
2758c2ecf20Sopenharmony_ci					adapter->link_duplex == FULL_DUPLEX ?
2768c2ecf20Sopenharmony_ci					"Full Duplex" : "Half Duplex");
2778c2ecf20Sopenharmony_ci		}
2788c2ecf20Sopenharmony_ci		if (!netif_carrier_ok(netdev))
2798c2ecf20Sopenharmony_ci			netif_carrier_on(netdev);
2808c2ecf20Sopenharmony_ci	}
2818c2ecf20Sopenharmony_ci}
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_cistatic void atl1c_link_chg_event(struct atl1c_adapter *adapter)
2848c2ecf20Sopenharmony_ci{
2858c2ecf20Sopenharmony_ci	struct net_device *netdev = adapter->netdev;
2868c2ecf20Sopenharmony_ci	struct pci_dev    *pdev   = adapter->pdev;
2878c2ecf20Sopenharmony_ci	u16 phy_data;
2888c2ecf20Sopenharmony_ci	u16 link_up;
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci	spin_lock(&adapter->mdio_lock);
2918c2ecf20Sopenharmony_ci	atl1c_read_phy_reg(&adapter->hw, MII_BMSR, &phy_data);
2928c2ecf20Sopenharmony_ci	atl1c_read_phy_reg(&adapter->hw, MII_BMSR, &phy_data);
2938c2ecf20Sopenharmony_ci	spin_unlock(&adapter->mdio_lock);
2948c2ecf20Sopenharmony_ci	link_up = phy_data & BMSR_LSTATUS;
2958c2ecf20Sopenharmony_ci	/* notify upper layer link down ASAP */
2968c2ecf20Sopenharmony_ci	if (!link_up) {
2978c2ecf20Sopenharmony_ci		if (netif_carrier_ok(netdev)) {
2988c2ecf20Sopenharmony_ci			/* old link state: Up */
2998c2ecf20Sopenharmony_ci			netif_carrier_off(netdev);
3008c2ecf20Sopenharmony_ci			if (netif_msg_link(adapter))
3018c2ecf20Sopenharmony_ci				dev_info(&pdev->dev,
3028c2ecf20Sopenharmony_ci					"%s: %s NIC Link is Down\n",
3038c2ecf20Sopenharmony_ci					atl1c_driver_name, netdev->name);
3048c2ecf20Sopenharmony_ci			adapter->link_speed = SPEED_0;
3058c2ecf20Sopenharmony_ci		}
3068c2ecf20Sopenharmony_ci	}
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci	set_bit(ATL1C_WORK_EVENT_LINK_CHANGE, &adapter->work_event);
3098c2ecf20Sopenharmony_ci	schedule_work(&adapter->common_task);
3108c2ecf20Sopenharmony_ci}
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_cistatic void atl1c_common_task(struct work_struct *work)
3138c2ecf20Sopenharmony_ci{
3148c2ecf20Sopenharmony_ci	struct atl1c_adapter *adapter;
3158c2ecf20Sopenharmony_ci	struct net_device *netdev;
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci	adapter = container_of(work, struct atl1c_adapter, common_task);
3188c2ecf20Sopenharmony_ci	netdev = adapter->netdev;
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci	if (test_bit(__AT_DOWN, &adapter->flags))
3218c2ecf20Sopenharmony_ci		return;
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci	if (test_and_clear_bit(ATL1C_WORK_EVENT_RESET, &adapter->work_event)) {
3248c2ecf20Sopenharmony_ci		netif_device_detach(netdev);
3258c2ecf20Sopenharmony_ci		atl1c_down(adapter);
3268c2ecf20Sopenharmony_ci		atl1c_up(adapter);
3278c2ecf20Sopenharmony_ci		netif_device_attach(netdev);
3288c2ecf20Sopenharmony_ci	}
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci	if (test_and_clear_bit(ATL1C_WORK_EVENT_LINK_CHANGE,
3318c2ecf20Sopenharmony_ci		&adapter->work_event)) {
3328c2ecf20Sopenharmony_ci		atl1c_irq_disable(adapter);
3338c2ecf20Sopenharmony_ci		atl1c_check_link_status(adapter);
3348c2ecf20Sopenharmony_ci		atl1c_irq_enable(adapter);
3358c2ecf20Sopenharmony_ci	}
3368c2ecf20Sopenharmony_ci}
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_cistatic void atl1c_del_timer(struct atl1c_adapter *adapter)
3408c2ecf20Sopenharmony_ci{
3418c2ecf20Sopenharmony_ci	del_timer_sync(&adapter->phy_config_timer);
3428c2ecf20Sopenharmony_ci}
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_ci/**
3468c2ecf20Sopenharmony_ci * atl1c_tx_timeout - Respond to a Tx Hang
3478c2ecf20Sopenharmony_ci * @netdev: network interface device structure
3488c2ecf20Sopenharmony_ci * @txqueue: index of hanging tx queue
3498c2ecf20Sopenharmony_ci */
3508c2ecf20Sopenharmony_cistatic void atl1c_tx_timeout(struct net_device *netdev, unsigned int txqueue)
3518c2ecf20Sopenharmony_ci{
3528c2ecf20Sopenharmony_ci	struct atl1c_adapter *adapter = netdev_priv(netdev);
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ci	/* Do the reset outside of interrupt context */
3558c2ecf20Sopenharmony_ci	set_bit(ATL1C_WORK_EVENT_RESET, &adapter->work_event);
3568c2ecf20Sopenharmony_ci	schedule_work(&adapter->common_task);
3578c2ecf20Sopenharmony_ci}
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci/**
3608c2ecf20Sopenharmony_ci * atl1c_set_multi - Multicast and Promiscuous mode set
3618c2ecf20Sopenharmony_ci * @netdev: network interface device structure
3628c2ecf20Sopenharmony_ci *
3638c2ecf20Sopenharmony_ci * The set_multi entry point is called whenever the multicast address
3648c2ecf20Sopenharmony_ci * list or the network interface flags are updated.  This routine is
3658c2ecf20Sopenharmony_ci * responsible for configuring the hardware for proper multicast,
3668c2ecf20Sopenharmony_ci * promiscuous mode, and all-multi behavior.
3678c2ecf20Sopenharmony_ci */
3688c2ecf20Sopenharmony_cistatic void atl1c_set_multi(struct net_device *netdev)
3698c2ecf20Sopenharmony_ci{
3708c2ecf20Sopenharmony_ci	struct atl1c_adapter *adapter = netdev_priv(netdev);
3718c2ecf20Sopenharmony_ci	struct atl1c_hw *hw = &adapter->hw;
3728c2ecf20Sopenharmony_ci	struct netdev_hw_addr *ha;
3738c2ecf20Sopenharmony_ci	u32 mac_ctrl_data;
3748c2ecf20Sopenharmony_ci	u32 hash_value;
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci	/* Check for Promiscuous and All Multicast modes */
3778c2ecf20Sopenharmony_ci	AT_READ_REG(hw, REG_MAC_CTRL, &mac_ctrl_data);
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci	if (netdev->flags & IFF_PROMISC) {
3808c2ecf20Sopenharmony_ci		mac_ctrl_data |= MAC_CTRL_PROMIS_EN;
3818c2ecf20Sopenharmony_ci	} else if (netdev->flags & IFF_ALLMULTI) {
3828c2ecf20Sopenharmony_ci		mac_ctrl_data |= MAC_CTRL_MC_ALL_EN;
3838c2ecf20Sopenharmony_ci		mac_ctrl_data &= ~MAC_CTRL_PROMIS_EN;
3848c2ecf20Sopenharmony_ci	} else {
3858c2ecf20Sopenharmony_ci		mac_ctrl_data &= ~(MAC_CTRL_PROMIS_EN | MAC_CTRL_MC_ALL_EN);
3868c2ecf20Sopenharmony_ci	}
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ci	AT_WRITE_REG(hw, REG_MAC_CTRL, mac_ctrl_data);
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_ci	/* clear the old settings from the multicast hash table */
3918c2ecf20Sopenharmony_ci	AT_WRITE_REG(hw, REG_RX_HASH_TABLE, 0);
3928c2ecf20Sopenharmony_ci	AT_WRITE_REG_ARRAY(hw, REG_RX_HASH_TABLE, 1, 0);
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_ci	/* comoute mc addresses' hash value ,and put it into hash table */
3958c2ecf20Sopenharmony_ci	netdev_for_each_mc_addr(ha, netdev) {
3968c2ecf20Sopenharmony_ci		hash_value = atl1c_hash_mc_addr(hw, ha->addr);
3978c2ecf20Sopenharmony_ci		atl1c_hash_set(hw, hash_value);
3988c2ecf20Sopenharmony_ci	}
3998c2ecf20Sopenharmony_ci}
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_cistatic void __atl1c_vlan_mode(netdev_features_t features, u32 *mac_ctrl_data)
4028c2ecf20Sopenharmony_ci{
4038c2ecf20Sopenharmony_ci	if (features & NETIF_F_HW_VLAN_CTAG_RX) {
4048c2ecf20Sopenharmony_ci		/* enable VLAN tag insert/strip */
4058c2ecf20Sopenharmony_ci		*mac_ctrl_data |= MAC_CTRL_RMV_VLAN;
4068c2ecf20Sopenharmony_ci	} else {
4078c2ecf20Sopenharmony_ci		/* disable VLAN tag insert/strip */
4088c2ecf20Sopenharmony_ci		*mac_ctrl_data &= ~MAC_CTRL_RMV_VLAN;
4098c2ecf20Sopenharmony_ci	}
4108c2ecf20Sopenharmony_ci}
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_cistatic void atl1c_vlan_mode(struct net_device *netdev,
4138c2ecf20Sopenharmony_ci	netdev_features_t features)
4148c2ecf20Sopenharmony_ci{
4158c2ecf20Sopenharmony_ci	struct atl1c_adapter *adapter = netdev_priv(netdev);
4168c2ecf20Sopenharmony_ci	struct pci_dev *pdev = adapter->pdev;
4178c2ecf20Sopenharmony_ci	u32 mac_ctrl_data = 0;
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ci	if (netif_msg_pktdata(adapter))
4208c2ecf20Sopenharmony_ci		dev_dbg(&pdev->dev, "atl1c_vlan_mode\n");
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci	atl1c_irq_disable(adapter);
4238c2ecf20Sopenharmony_ci	AT_READ_REG(&adapter->hw, REG_MAC_CTRL, &mac_ctrl_data);
4248c2ecf20Sopenharmony_ci	__atl1c_vlan_mode(features, &mac_ctrl_data);
4258c2ecf20Sopenharmony_ci	AT_WRITE_REG(&adapter->hw, REG_MAC_CTRL, mac_ctrl_data);
4268c2ecf20Sopenharmony_ci	atl1c_irq_enable(adapter);
4278c2ecf20Sopenharmony_ci}
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_cistatic void atl1c_restore_vlan(struct atl1c_adapter *adapter)
4308c2ecf20Sopenharmony_ci{
4318c2ecf20Sopenharmony_ci	struct pci_dev *pdev = adapter->pdev;
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_ci	if (netif_msg_pktdata(adapter))
4348c2ecf20Sopenharmony_ci		dev_dbg(&pdev->dev, "atl1c_restore_vlan\n");
4358c2ecf20Sopenharmony_ci	atl1c_vlan_mode(adapter->netdev, adapter->netdev->features);
4368c2ecf20Sopenharmony_ci}
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_ci/**
4398c2ecf20Sopenharmony_ci * atl1c_set_mac - Change the Ethernet Address of the NIC
4408c2ecf20Sopenharmony_ci * @netdev: network interface device structure
4418c2ecf20Sopenharmony_ci * @p: pointer to an address structure
4428c2ecf20Sopenharmony_ci *
4438c2ecf20Sopenharmony_ci * Returns 0 on success, negative on failure
4448c2ecf20Sopenharmony_ci */
4458c2ecf20Sopenharmony_cistatic int atl1c_set_mac_addr(struct net_device *netdev, void *p)
4468c2ecf20Sopenharmony_ci{
4478c2ecf20Sopenharmony_ci	struct atl1c_adapter *adapter = netdev_priv(netdev);
4488c2ecf20Sopenharmony_ci	struct sockaddr *addr = p;
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ci	if (!is_valid_ether_addr(addr->sa_data))
4518c2ecf20Sopenharmony_ci		return -EADDRNOTAVAIL;
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci	if (netif_running(netdev))
4548c2ecf20Sopenharmony_ci		return -EBUSY;
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_ci	memcpy(netdev->dev_addr, addr->sa_data, netdev->addr_len);
4578c2ecf20Sopenharmony_ci	memcpy(adapter->hw.mac_addr, addr->sa_data, netdev->addr_len);
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_ci	atl1c_hw_set_mac_addr(&adapter->hw, adapter->hw.mac_addr);
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_ci	return 0;
4628c2ecf20Sopenharmony_ci}
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_cistatic void atl1c_set_rxbufsize(struct atl1c_adapter *adapter,
4658c2ecf20Sopenharmony_ci				struct net_device *dev)
4668c2ecf20Sopenharmony_ci{
4678c2ecf20Sopenharmony_ci	unsigned int head_size;
4688c2ecf20Sopenharmony_ci	int mtu = dev->mtu;
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_ci	adapter->rx_buffer_len = mtu > AT_RX_BUF_SIZE ?
4718c2ecf20Sopenharmony_ci		roundup(mtu + ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN, 8) : AT_RX_BUF_SIZE;
4728c2ecf20Sopenharmony_ci
4738c2ecf20Sopenharmony_ci	head_size = SKB_DATA_ALIGN(adapter->rx_buffer_len + NET_SKB_PAD) +
4748c2ecf20Sopenharmony_ci		    SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
4758c2ecf20Sopenharmony_ci	adapter->rx_frag_size = roundup_pow_of_two(head_size);
4768c2ecf20Sopenharmony_ci}
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_cistatic netdev_features_t atl1c_fix_features(struct net_device *netdev,
4798c2ecf20Sopenharmony_ci	netdev_features_t features)
4808c2ecf20Sopenharmony_ci{
4818c2ecf20Sopenharmony_ci	/*
4828c2ecf20Sopenharmony_ci	 * Since there is no support for separate rx/tx vlan accel
4838c2ecf20Sopenharmony_ci	 * enable/disable make sure tx flag is always in same state as rx.
4848c2ecf20Sopenharmony_ci	 */
4858c2ecf20Sopenharmony_ci	if (features & NETIF_F_HW_VLAN_CTAG_RX)
4868c2ecf20Sopenharmony_ci		features |= NETIF_F_HW_VLAN_CTAG_TX;
4878c2ecf20Sopenharmony_ci	else
4888c2ecf20Sopenharmony_ci		features &= ~NETIF_F_HW_VLAN_CTAG_TX;
4898c2ecf20Sopenharmony_ci
4908c2ecf20Sopenharmony_ci	if (netdev->mtu > MAX_TSO_FRAME_SIZE)
4918c2ecf20Sopenharmony_ci		features &= ~(NETIF_F_TSO | NETIF_F_TSO6);
4928c2ecf20Sopenharmony_ci
4938c2ecf20Sopenharmony_ci	return features;
4948c2ecf20Sopenharmony_ci}
4958c2ecf20Sopenharmony_ci
4968c2ecf20Sopenharmony_cistatic int atl1c_set_features(struct net_device *netdev,
4978c2ecf20Sopenharmony_ci	netdev_features_t features)
4988c2ecf20Sopenharmony_ci{
4998c2ecf20Sopenharmony_ci	netdev_features_t changed = netdev->features ^ features;
5008c2ecf20Sopenharmony_ci
5018c2ecf20Sopenharmony_ci	if (changed & NETIF_F_HW_VLAN_CTAG_RX)
5028c2ecf20Sopenharmony_ci		atl1c_vlan_mode(netdev, features);
5038c2ecf20Sopenharmony_ci
5048c2ecf20Sopenharmony_ci	return 0;
5058c2ecf20Sopenharmony_ci}
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_cistatic void atl1c_set_max_mtu(struct net_device *netdev)
5088c2ecf20Sopenharmony_ci{
5098c2ecf20Sopenharmony_ci	struct atl1c_adapter *adapter = netdev_priv(netdev);
5108c2ecf20Sopenharmony_ci	struct atl1c_hw *hw = &adapter->hw;
5118c2ecf20Sopenharmony_ci
5128c2ecf20Sopenharmony_ci	switch (hw->nic_type) {
5138c2ecf20Sopenharmony_ci	/* These (GbE) devices support jumbo packets, max_mtu 6122 */
5148c2ecf20Sopenharmony_ci	case athr_l1c:
5158c2ecf20Sopenharmony_ci	case athr_l1d:
5168c2ecf20Sopenharmony_ci	case athr_l1d_2:
5178c2ecf20Sopenharmony_ci		netdev->max_mtu = MAX_JUMBO_FRAME_SIZE -
5188c2ecf20Sopenharmony_ci				  (ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN);
5198c2ecf20Sopenharmony_ci		break;
5208c2ecf20Sopenharmony_ci	/* The 10/100 devices don't support jumbo packets, max_mtu 1500 */
5218c2ecf20Sopenharmony_ci	default:
5228c2ecf20Sopenharmony_ci		netdev->max_mtu = ETH_DATA_LEN;
5238c2ecf20Sopenharmony_ci		break;
5248c2ecf20Sopenharmony_ci	}
5258c2ecf20Sopenharmony_ci}
5268c2ecf20Sopenharmony_ci
5278c2ecf20Sopenharmony_ci/**
5288c2ecf20Sopenharmony_ci * atl1c_change_mtu - Change the Maximum Transfer Unit
5298c2ecf20Sopenharmony_ci * @netdev: network interface device structure
5308c2ecf20Sopenharmony_ci * @new_mtu: new value for maximum frame size
5318c2ecf20Sopenharmony_ci *
5328c2ecf20Sopenharmony_ci * Returns 0 on success, negative on failure
5338c2ecf20Sopenharmony_ci */
5348c2ecf20Sopenharmony_cistatic int atl1c_change_mtu(struct net_device *netdev, int new_mtu)
5358c2ecf20Sopenharmony_ci{
5368c2ecf20Sopenharmony_ci	struct atl1c_adapter *adapter = netdev_priv(netdev);
5378c2ecf20Sopenharmony_ci
5388c2ecf20Sopenharmony_ci	/* set MTU */
5398c2ecf20Sopenharmony_ci	if (netif_running(netdev)) {
5408c2ecf20Sopenharmony_ci		while (test_and_set_bit(__AT_RESETTING, &adapter->flags))
5418c2ecf20Sopenharmony_ci			msleep(1);
5428c2ecf20Sopenharmony_ci		netdev->mtu = new_mtu;
5438c2ecf20Sopenharmony_ci		adapter->hw.max_frame_size = new_mtu;
5448c2ecf20Sopenharmony_ci		atl1c_set_rxbufsize(adapter, netdev);
5458c2ecf20Sopenharmony_ci		atl1c_down(adapter);
5468c2ecf20Sopenharmony_ci		netdev_update_features(netdev);
5478c2ecf20Sopenharmony_ci		atl1c_up(adapter);
5488c2ecf20Sopenharmony_ci		clear_bit(__AT_RESETTING, &adapter->flags);
5498c2ecf20Sopenharmony_ci	}
5508c2ecf20Sopenharmony_ci	return 0;
5518c2ecf20Sopenharmony_ci}
5528c2ecf20Sopenharmony_ci
5538c2ecf20Sopenharmony_ci/*
5548c2ecf20Sopenharmony_ci *  caller should hold mdio_lock
5558c2ecf20Sopenharmony_ci */
5568c2ecf20Sopenharmony_cistatic int atl1c_mdio_read(struct net_device *netdev, int phy_id, int reg_num)
5578c2ecf20Sopenharmony_ci{
5588c2ecf20Sopenharmony_ci	struct atl1c_adapter *adapter = netdev_priv(netdev);
5598c2ecf20Sopenharmony_ci	u16 result;
5608c2ecf20Sopenharmony_ci
5618c2ecf20Sopenharmony_ci	atl1c_read_phy_reg(&adapter->hw, reg_num, &result);
5628c2ecf20Sopenharmony_ci	return result;
5638c2ecf20Sopenharmony_ci}
5648c2ecf20Sopenharmony_ci
5658c2ecf20Sopenharmony_cistatic void atl1c_mdio_write(struct net_device *netdev, int phy_id,
5668c2ecf20Sopenharmony_ci			     int reg_num, int val)
5678c2ecf20Sopenharmony_ci{
5688c2ecf20Sopenharmony_ci	struct atl1c_adapter *adapter = netdev_priv(netdev);
5698c2ecf20Sopenharmony_ci
5708c2ecf20Sopenharmony_ci	atl1c_write_phy_reg(&adapter->hw, reg_num, val);
5718c2ecf20Sopenharmony_ci}
5728c2ecf20Sopenharmony_ci
5738c2ecf20Sopenharmony_cistatic int atl1c_mii_ioctl(struct net_device *netdev,
5748c2ecf20Sopenharmony_ci			   struct ifreq *ifr, int cmd)
5758c2ecf20Sopenharmony_ci{
5768c2ecf20Sopenharmony_ci	struct atl1c_adapter *adapter = netdev_priv(netdev);
5778c2ecf20Sopenharmony_ci	struct pci_dev *pdev = adapter->pdev;
5788c2ecf20Sopenharmony_ci	struct mii_ioctl_data *data = if_mii(ifr);
5798c2ecf20Sopenharmony_ci	unsigned long flags;
5808c2ecf20Sopenharmony_ci	int retval = 0;
5818c2ecf20Sopenharmony_ci
5828c2ecf20Sopenharmony_ci	if (!netif_running(netdev))
5838c2ecf20Sopenharmony_ci		return -EINVAL;
5848c2ecf20Sopenharmony_ci
5858c2ecf20Sopenharmony_ci	spin_lock_irqsave(&adapter->mdio_lock, flags);
5868c2ecf20Sopenharmony_ci	switch (cmd) {
5878c2ecf20Sopenharmony_ci	case SIOCGMIIPHY:
5888c2ecf20Sopenharmony_ci		data->phy_id = 0;
5898c2ecf20Sopenharmony_ci		break;
5908c2ecf20Sopenharmony_ci
5918c2ecf20Sopenharmony_ci	case SIOCGMIIREG:
5928c2ecf20Sopenharmony_ci		if (atl1c_read_phy_reg(&adapter->hw, data->reg_num & 0x1F,
5938c2ecf20Sopenharmony_ci				    &data->val_out)) {
5948c2ecf20Sopenharmony_ci			retval = -EIO;
5958c2ecf20Sopenharmony_ci			goto out;
5968c2ecf20Sopenharmony_ci		}
5978c2ecf20Sopenharmony_ci		break;
5988c2ecf20Sopenharmony_ci
5998c2ecf20Sopenharmony_ci	case SIOCSMIIREG:
6008c2ecf20Sopenharmony_ci		if (data->reg_num & ~(0x1F)) {
6018c2ecf20Sopenharmony_ci			retval = -EFAULT;
6028c2ecf20Sopenharmony_ci			goto out;
6038c2ecf20Sopenharmony_ci		}
6048c2ecf20Sopenharmony_ci
6058c2ecf20Sopenharmony_ci		dev_dbg(&pdev->dev, "<atl1c_mii_ioctl> write %x %x",
6068c2ecf20Sopenharmony_ci				data->reg_num, data->val_in);
6078c2ecf20Sopenharmony_ci		if (atl1c_write_phy_reg(&adapter->hw,
6088c2ecf20Sopenharmony_ci				     data->reg_num, data->val_in)) {
6098c2ecf20Sopenharmony_ci			retval = -EIO;
6108c2ecf20Sopenharmony_ci			goto out;
6118c2ecf20Sopenharmony_ci		}
6128c2ecf20Sopenharmony_ci		break;
6138c2ecf20Sopenharmony_ci
6148c2ecf20Sopenharmony_ci	default:
6158c2ecf20Sopenharmony_ci		retval = -EOPNOTSUPP;
6168c2ecf20Sopenharmony_ci		break;
6178c2ecf20Sopenharmony_ci	}
6188c2ecf20Sopenharmony_ciout:
6198c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&adapter->mdio_lock, flags);
6208c2ecf20Sopenharmony_ci	return retval;
6218c2ecf20Sopenharmony_ci}
6228c2ecf20Sopenharmony_ci
6238c2ecf20Sopenharmony_cistatic int atl1c_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd)
6248c2ecf20Sopenharmony_ci{
6258c2ecf20Sopenharmony_ci	switch (cmd) {
6268c2ecf20Sopenharmony_ci	case SIOCGMIIPHY:
6278c2ecf20Sopenharmony_ci	case SIOCGMIIREG:
6288c2ecf20Sopenharmony_ci	case SIOCSMIIREG:
6298c2ecf20Sopenharmony_ci		return atl1c_mii_ioctl(netdev, ifr, cmd);
6308c2ecf20Sopenharmony_ci	default:
6318c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
6328c2ecf20Sopenharmony_ci	}
6338c2ecf20Sopenharmony_ci}
6348c2ecf20Sopenharmony_ci
6358c2ecf20Sopenharmony_ci/**
6368c2ecf20Sopenharmony_ci * atl1c_alloc_queues - Allocate memory for all rings
6378c2ecf20Sopenharmony_ci * @adapter: board private structure to initialize
6388c2ecf20Sopenharmony_ci *
6398c2ecf20Sopenharmony_ci */
6408c2ecf20Sopenharmony_cistatic int atl1c_alloc_queues(struct atl1c_adapter *adapter)
6418c2ecf20Sopenharmony_ci{
6428c2ecf20Sopenharmony_ci	return 0;
6438c2ecf20Sopenharmony_ci}
6448c2ecf20Sopenharmony_ci
6458c2ecf20Sopenharmony_cistatic void atl1c_set_mac_type(struct atl1c_hw *hw)
6468c2ecf20Sopenharmony_ci{
6478c2ecf20Sopenharmony_ci	switch (hw->device_id) {
6488c2ecf20Sopenharmony_ci	case PCI_DEVICE_ID_ATTANSIC_L2C:
6498c2ecf20Sopenharmony_ci		hw->nic_type = athr_l2c;
6508c2ecf20Sopenharmony_ci		break;
6518c2ecf20Sopenharmony_ci	case PCI_DEVICE_ID_ATTANSIC_L1C:
6528c2ecf20Sopenharmony_ci		hw->nic_type = athr_l1c;
6538c2ecf20Sopenharmony_ci		break;
6548c2ecf20Sopenharmony_ci	case PCI_DEVICE_ID_ATHEROS_L2C_B:
6558c2ecf20Sopenharmony_ci		hw->nic_type = athr_l2c_b;
6568c2ecf20Sopenharmony_ci		break;
6578c2ecf20Sopenharmony_ci	case PCI_DEVICE_ID_ATHEROS_L2C_B2:
6588c2ecf20Sopenharmony_ci		hw->nic_type = athr_l2c_b2;
6598c2ecf20Sopenharmony_ci		break;
6608c2ecf20Sopenharmony_ci	case PCI_DEVICE_ID_ATHEROS_L1D:
6618c2ecf20Sopenharmony_ci		hw->nic_type = athr_l1d;
6628c2ecf20Sopenharmony_ci		break;
6638c2ecf20Sopenharmony_ci	case PCI_DEVICE_ID_ATHEROS_L1D_2_0:
6648c2ecf20Sopenharmony_ci		hw->nic_type = athr_l1d_2;
6658c2ecf20Sopenharmony_ci		break;
6668c2ecf20Sopenharmony_ci	default:
6678c2ecf20Sopenharmony_ci		break;
6688c2ecf20Sopenharmony_ci	}
6698c2ecf20Sopenharmony_ci}
6708c2ecf20Sopenharmony_ci
6718c2ecf20Sopenharmony_cistatic int atl1c_setup_mac_funcs(struct atl1c_hw *hw)
6728c2ecf20Sopenharmony_ci{
6738c2ecf20Sopenharmony_ci	u32 link_ctrl_data;
6748c2ecf20Sopenharmony_ci
6758c2ecf20Sopenharmony_ci	atl1c_set_mac_type(hw);
6768c2ecf20Sopenharmony_ci	AT_READ_REG(hw, REG_LINK_CTRL, &link_ctrl_data);
6778c2ecf20Sopenharmony_ci
6788c2ecf20Sopenharmony_ci	hw->ctrl_flags = ATL1C_INTR_MODRT_ENABLE  |
6798c2ecf20Sopenharmony_ci			 ATL1C_TXQ_MODE_ENHANCE;
6808c2ecf20Sopenharmony_ci	hw->ctrl_flags |= ATL1C_ASPM_L0S_SUPPORT |
6818c2ecf20Sopenharmony_ci			  ATL1C_ASPM_L1_SUPPORT;
6828c2ecf20Sopenharmony_ci	hw->ctrl_flags |= ATL1C_ASPM_CTRL_MON;
6838c2ecf20Sopenharmony_ci
6848c2ecf20Sopenharmony_ci	if (hw->nic_type == athr_l1c ||
6858c2ecf20Sopenharmony_ci	    hw->nic_type == athr_l1d ||
6868c2ecf20Sopenharmony_ci	    hw->nic_type == athr_l1d_2)
6878c2ecf20Sopenharmony_ci		hw->link_cap_flags |= ATL1C_LINK_CAP_1000M;
6888c2ecf20Sopenharmony_ci	return 0;
6898c2ecf20Sopenharmony_ci}
6908c2ecf20Sopenharmony_ci
6918c2ecf20Sopenharmony_cistruct atl1c_platform_patch {
6928c2ecf20Sopenharmony_ci	u16 pci_did;
6938c2ecf20Sopenharmony_ci	u8  pci_revid;
6948c2ecf20Sopenharmony_ci	u16 subsystem_vid;
6958c2ecf20Sopenharmony_ci	u16 subsystem_did;
6968c2ecf20Sopenharmony_ci	u32 patch_flag;
6978c2ecf20Sopenharmony_ci#define ATL1C_LINK_PATCH	0x1
6988c2ecf20Sopenharmony_ci};
6998c2ecf20Sopenharmony_cistatic const struct atl1c_platform_patch plats[] = {
7008c2ecf20Sopenharmony_ci{0x2060, 0xC1, 0x1019, 0x8152, 0x1},
7018c2ecf20Sopenharmony_ci{0x2060, 0xC1, 0x1019, 0x2060, 0x1},
7028c2ecf20Sopenharmony_ci{0x2060, 0xC1, 0x1019, 0xE000, 0x1},
7038c2ecf20Sopenharmony_ci{0x2062, 0xC0, 0x1019, 0x8152, 0x1},
7048c2ecf20Sopenharmony_ci{0x2062, 0xC0, 0x1019, 0x2062, 0x1},
7058c2ecf20Sopenharmony_ci{0x2062, 0xC0, 0x1458, 0xE000, 0x1},
7068c2ecf20Sopenharmony_ci{0x2062, 0xC1, 0x1019, 0x8152, 0x1},
7078c2ecf20Sopenharmony_ci{0x2062, 0xC1, 0x1019, 0x2062, 0x1},
7088c2ecf20Sopenharmony_ci{0x2062, 0xC1, 0x1458, 0xE000, 0x1},
7098c2ecf20Sopenharmony_ci{0x2062, 0xC1, 0x1565, 0x2802, 0x1},
7108c2ecf20Sopenharmony_ci{0x2062, 0xC1, 0x1565, 0x2801, 0x1},
7118c2ecf20Sopenharmony_ci{0x1073, 0xC0, 0x1019, 0x8151, 0x1},
7128c2ecf20Sopenharmony_ci{0x1073, 0xC0, 0x1019, 0x1073, 0x1},
7138c2ecf20Sopenharmony_ci{0x1073, 0xC0, 0x1458, 0xE000, 0x1},
7148c2ecf20Sopenharmony_ci{0x1083, 0xC0, 0x1458, 0xE000, 0x1},
7158c2ecf20Sopenharmony_ci{0x1083, 0xC0, 0x1019, 0x8151, 0x1},
7168c2ecf20Sopenharmony_ci{0x1083, 0xC0, 0x1019, 0x1083, 0x1},
7178c2ecf20Sopenharmony_ci{0x1083, 0xC0, 0x1462, 0x7680, 0x1},
7188c2ecf20Sopenharmony_ci{0x1083, 0xC0, 0x1565, 0x2803, 0x1},
7198c2ecf20Sopenharmony_ci{0},
7208c2ecf20Sopenharmony_ci};
7218c2ecf20Sopenharmony_ci
7228c2ecf20Sopenharmony_cistatic void atl1c_patch_assign(struct atl1c_hw *hw)
7238c2ecf20Sopenharmony_ci{
7248c2ecf20Sopenharmony_ci	struct pci_dev	*pdev = hw->adapter->pdev;
7258c2ecf20Sopenharmony_ci	u32 misc_ctrl;
7268c2ecf20Sopenharmony_ci	int i = 0;
7278c2ecf20Sopenharmony_ci
7288c2ecf20Sopenharmony_ci	hw->msi_lnkpatch = false;
7298c2ecf20Sopenharmony_ci
7308c2ecf20Sopenharmony_ci	while (plats[i].pci_did != 0) {
7318c2ecf20Sopenharmony_ci		if (plats[i].pci_did == hw->device_id &&
7328c2ecf20Sopenharmony_ci		    plats[i].pci_revid == hw->revision_id &&
7338c2ecf20Sopenharmony_ci		    plats[i].subsystem_vid == hw->subsystem_vendor_id &&
7348c2ecf20Sopenharmony_ci		    plats[i].subsystem_did == hw->subsystem_id) {
7358c2ecf20Sopenharmony_ci			if (plats[i].patch_flag & ATL1C_LINK_PATCH)
7368c2ecf20Sopenharmony_ci				hw->msi_lnkpatch = true;
7378c2ecf20Sopenharmony_ci		}
7388c2ecf20Sopenharmony_ci		i++;
7398c2ecf20Sopenharmony_ci	}
7408c2ecf20Sopenharmony_ci
7418c2ecf20Sopenharmony_ci	if (hw->device_id == PCI_DEVICE_ID_ATHEROS_L2C_B2 &&
7428c2ecf20Sopenharmony_ci	    hw->revision_id == L2CB_V21) {
7438c2ecf20Sopenharmony_ci		/* config access mode */
7448c2ecf20Sopenharmony_ci		pci_write_config_dword(pdev, REG_PCIE_IND_ACC_ADDR,
7458c2ecf20Sopenharmony_ci				       REG_PCIE_DEV_MISC_CTRL);
7468c2ecf20Sopenharmony_ci		pci_read_config_dword(pdev, REG_PCIE_IND_ACC_DATA, &misc_ctrl);
7478c2ecf20Sopenharmony_ci		misc_ctrl &= ~0x100;
7488c2ecf20Sopenharmony_ci		pci_write_config_dword(pdev, REG_PCIE_IND_ACC_ADDR,
7498c2ecf20Sopenharmony_ci				       REG_PCIE_DEV_MISC_CTRL);
7508c2ecf20Sopenharmony_ci		pci_write_config_dword(pdev, REG_PCIE_IND_ACC_DATA, misc_ctrl);
7518c2ecf20Sopenharmony_ci	}
7528c2ecf20Sopenharmony_ci}
7538c2ecf20Sopenharmony_ci/**
7548c2ecf20Sopenharmony_ci * atl1c_sw_init - Initialize general software structures (struct atl1c_adapter)
7558c2ecf20Sopenharmony_ci * @adapter: board private structure to initialize
7568c2ecf20Sopenharmony_ci *
7578c2ecf20Sopenharmony_ci * atl1c_sw_init initializes the Adapter private data structure.
7588c2ecf20Sopenharmony_ci * Fields are initialized based on PCI device information and
7598c2ecf20Sopenharmony_ci * OS network device settings (MTU size).
7608c2ecf20Sopenharmony_ci */
7618c2ecf20Sopenharmony_cistatic int atl1c_sw_init(struct atl1c_adapter *adapter)
7628c2ecf20Sopenharmony_ci{
7638c2ecf20Sopenharmony_ci	struct atl1c_hw *hw   = &adapter->hw;
7648c2ecf20Sopenharmony_ci	struct pci_dev	*pdev = adapter->pdev;
7658c2ecf20Sopenharmony_ci	u32 revision;
7668c2ecf20Sopenharmony_ci
7678c2ecf20Sopenharmony_ci
7688c2ecf20Sopenharmony_ci	adapter->wol = 0;
7698c2ecf20Sopenharmony_ci	device_set_wakeup_enable(&pdev->dev, false);
7708c2ecf20Sopenharmony_ci	adapter->link_speed = SPEED_0;
7718c2ecf20Sopenharmony_ci	adapter->link_duplex = FULL_DUPLEX;
7728c2ecf20Sopenharmony_ci	adapter->tpd_ring[0].count = 1024;
7738c2ecf20Sopenharmony_ci	adapter->rfd_ring.count = 512;
7748c2ecf20Sopenharmony_ci
7758c2ecf20Sopenharmony_ci	hw->vendor_id = pdev->vendor;
7768c2ecf20Sopenharmony_ci	hw->device_id = pdev->device;
7778c2ecf20Sopenharmony_ci	hw->subsystem_vendor_id = pdev->subsystem_vendor;
7788c2ecf20Sopenharmony_ci	hw->subsystem_id = pdev->subsystem_device;
7798c2ecf20Sopenharmony_ci	pci_read_config_dword(pdev, PCI_CLASS_REVISION, &revision);
7808c2ecf20Sopenharmony_ci	hw->revision_id = revision & 0xFF;
7818c2ecf20Sopenharmony_ci	/* before link up, we assume hibernate is true */
7828c2ecf20Sopenharmony_ci	hw->hibernate = true;
7838c2ecf20Sopenharmony_ci	hw->media_type = MEDIA_TYPE_AUTO_SENSOR;
7848c2ecf20Sopenharmony_ci	if (atl1c_setup_mac_funcs(hw) != 0) {
7858c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "set mac function pointers failed\n");
7868c2ecf20Sopenharmony_ci		return -1;
7878c2ecf20Sopenharmony_ci	}
7888c2ecf20Sopenharmony_ci	atl1c_patch_assign(hw);
7898c2ecf20Sopenharmony_ci
7908c2ecf20Sopenharmony_ci	hw->intr_mask = IMR_NORMAL_MASK;
7918c2ecf20Sopenharmony_ci	hw->phy_configured = false;
7928c2ecf20Sopenharmony_ci	hw->preamble_len = 7;
7938c2ecf20Sopenharmony_ci	hw->max_frame_size = adapter->netdev->mtu;
7948c2ecf20Sopenharmony_ci	hw->autoneg_advertised = ADVERTISED_Autoneg;
7958c2ecf20Sopenharmony_ci	hw->indirect_tab = 0xE4E4E4E4;
7968c2ecf20Sopenharmony_ci	hw->base_cpu = 0;
7978c2ecf20Sopenharmony_ci
7988c2ecf20Sopenharmony_ci	hw->ict = 50000;		/* 100ms */
7998c2ecf20Sopenharmony_ci	hw->smb_timer = 200000;	  	/* 400ms */
8008c2ecf20Sopenharmony_ci	hw->rx_imt = 200;
8018c2ecf20Sopenharmony_ci	hw->tx_imt = 1000;
8028c2ecf20Sopenharmony_ci
8038c2ecf20Sopenharmony_ci	hw->tpd_burst = 5;
8048c2ecf20Sopenharmony_ci	hw->rfd_burst = 8;
8058c2ecf20Sopenharmony_ci	hw->dma_order = atl1c_dma_ord_out;
8068c2ecf20Sopenharmony_ci	hw->dmar_block = atl1c_dma_req_1024;
8078c2ecf20Sopenharmony_ci
8088c2ecf20Sopenharmony_ci	if (atl1c_alloc_queues(adapter)) {
8098c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "Unable to allocate memory for queues\n");
8108c2ecf20Sopenharmony_ci		return -ENOMEM;
8118c2ecf20Sopenharmony_ci	}
8128c2ecf20Sopenharmony_ci	/* TODO */
8138c2ecf20Sopenharmony_ci	atl1c_set_rxbufsize(adapter, adapter->netdev);
8148c2ecf20Sopenharmony_ci	atomic_set(&adapter->irq_sem, 1);
8158c2ecf20Sopenharmony_ci	spin_lock_init(&adapter->mdio_lock);
8168c2ecf20Sopenharmony_ci	set_bit(__AT_DOWN, &adapter->flags);
8178c2ecf20Sopenharmony_ci
8188c2ecf20Sopenharmony_ci	return 0;
8198c2ecf20Sopenharmony_ci}
8208c2ecf20Sopenharmony_ci
8218c2ecf20Sopenharmony_cistatic inline void atl1c_clean_buffer(struct pci_dev *pdev,
8228c2ecf20Sopenharmony_ci				struct atl1c_buffer *buffer_info)
8238c2ecf20Sopenharmony_ci{
8248c2ecf20Sopenharmony_ci	u16 pci_driection;
8258c2ecf20Sopenharmony_ci	if (buffer_info->flags & ATL1C_BUFFER_FREE)
8268c2ecf20Sopenharmony_ci		return;
8278c2ecf20Sopenharmony_ci	if (buffer_info->dma) {
8288c2ecf20Sopenharmony_ci		if (buffer_info->flags & ATL1C_PCIMAP_FROMDEVICE)
8298c2ecf20Sopenharmony_ci			pci_driection = DMA_FROM_DEVICE;
8308c2ecf20Sopenharmony_ci		else
8318c2ecf20Sopenharmony_ci			pci_driection = DMA_TO_DEVICE;
8328c2ecf20Sopenharmony_ci
8338c2ecf20Sopenharmony_ci		if (buffer_info->flags & ATL1C_PCIMAP_SINGLE)
8348c2ecf20Sopenharmony_ci			dma_unmap_single(&pdev->dev, buffer_info->dma,
8358c2ecf20Sopenharmony_ci					 buffer_info->length, pci_driection);
8368c2ecf20Sopenharmony_ci		else if (buffer_info->flags & ATL1C_PCIMAP_PAGE)
8378c2ecf20Sopenharmony_ci			dma_unmap_page(&pdev->dev, buffer_info->dma,
8388c2ecf20Sopenharmony_ci				       buffer_info->length, pci_driection);
8398c2ecf20Sopenharmony_ci	}
8408c2ecf20Sopenharmony_ci	if (buffer_info->skb)
8418c2ecf20Sopenharmony_ci		dev_consume_skb_any(buffer_info->skb);
8428c2ecf20Sopenharmony_ci	buffer_info->dma = 0;
8438c2ecf20Sopenharmony_ci	buffer_info->skb = NULL;
8448c2ecf20Sopenharmony_ci	ATL1C_SET_BUFFER_STATE(buffer_info, ATL1C_BUFFER_FREE);
8458c2ecf20Sopenharmony_ci}
8468c2ecf20Sopenharmony_ci/**
8478c2ecf20Sopenharmony_ci * atl1c_clean_tx_ring - Free Tx-skb
8488c2ecf20Sopenharmony_ci * @adapter: board private structure
8498c2ecf20Sopenharmony_ci * @type: type of transmit queue
8508c2ecf20Sopenharmony_ci */
8518c2ecf20Sopenharmony_cistatic void atl1c_clean_tx_ring(struct atl1c_adapter *adapter,
8528c2ecf20Sopenharmony_ci				enum atl1c_trans_queue type)
8538c2ecf20Sopenharmony_ci{
8548c2ecf20Sopenharmony_ci	struct atl1c_tpd_ring *tpd_ring = &adapter->tpd_ring[type];
8558c2ecf20Sopenharmony_ci	struct atl1c_buffer *buffer_info;
8568c2ecf20Sopenharmony_ci	struct pci_dev *pdev = adapter->pdev;
8578c2ecf20Sopenharmony_ci	u16 index, ring_count;
8588c2ecf20Sopenharmony_ci
8598c2ecf20Sopenharmony_ci	ring_count = tpd_ring->count;
8608c2ecf20Sopenharmony_ci	for (index = 0; index < ring_count; index++) {
8618c2ecf20Sopenharmony_ci		buffer_info = &tpd_ring->buffer_info[index];
8628c2ecf20Sopenharmony_ci		atl1c_clean_buffer(pdev, buffer_info);
8638c2ecf20Sopenharmony_ci	}
8648c2ecf20Sopenharmony_ci
8658c2ecf20Sopenharmony_ci	netdev_reset_queue(adapter->netdev);
8668c2ecf20Sopenharmony_ci
8678c2ecf20Sopenharmony_ci	/* Zero out Tx-buffers */
8688c2ecf20Sopenharmony_ci	memset(tpd_ring->desc, 0, sizeof(struct atl1c_tpd_desc) *
8698c2ecf20Sopenharmony_ci		ring_count);
8708c2ecf20Sopenharmony_ci	atomic_set(&tpd_ring->next_to_clean, 0);
8718c2ecf20Sopenharmony_ci	tpd_ring->next_to_use = 0;
8728c2ecf20Sopenharmony_ci}
8738c2ecf20Sopenharmony_ci
8748c2ecf20Sopenharmony_ci/**
8758c2ecf20Sopenharmony_ci * atl1c_clean_rx_ring - Free rx-reservation skbs
8768c2ecf20Sopenharmony_ci * @adapter: board private structure
8778c2ecf20Sopenharmony_ci */
8788c2ecf20Sopenharmony_cistatic void atl1c_clean_rx_ring(struct atl1c_adapter *adapter)
8798c2ecf20Sopenharmony_ci{
8808c2ecf20Sopenharmony_ci	struct atl1c_rfd_ring *rfd_ring = &adapter->rfd_ring;
8818c2ecf20Sopenharmony_ci	struct atl1c_rrd_ring *rrd_ring = &adapter->rrd_ring;
8828c2ecf20Sopenharmony_ci	struct atl1c_buffer *buffer_info;
8838c2ecf20Sopenharmony_ci	struct pci_dev *pdev = adapter->pdev;
8848c2ecf20Sopenharmony_ci	int j;
8858c2ecf20Sopenharmony_ci
8868c2ecf20Sopenharmony_ci	for (j = 0; j < rfd_ring->count; j++) {
8878c2ecf20Sopenharmony_ci		buffer_info = &rfd_ring->buffer_info[j];
8888c2ecf20Sopenharmony_ci		atl1c_clean_buffer(pdev, buffer_info);
8898c2ecf20Sopenharmony_ci	}
8908c2ecf20Sopenharmony_ci	/* zero out the descriptor ring */
8918c2ecf20Sopenharmony_ci	memset(rfd_ring->desc, 0, rfd_ring->size);
8928c2ecf20Sopenharmony_ci	rfd_ring->next_to_clean = 0;
8938c2ecf20Sopenharmony_ci	rfd_ring->next_to_use = 0;
8948c2ecf20Sopenharmony_ci	rrd_ring->next_to_use = 0;
8958c2ecf20Sopenharmony_ci	rrd_ring->next_to_clean = 0;
8968c2ecf20Sopenharmony_ci}
8978c2ecf20Sopenharmony_ci
8988c2ecf20Sopenharmony_ci/*
8998c2ecf20Sopenharmony_ci * Read / Write Ptr Initialize:
9008c2ecf20Sopenharmony_ci */
9018c2ecf20Sopenharmony_cistatic void atl1c_init_ring_ptrs(struct atl1c_adapter *adapter)
9028c2ecf20Sopenharmony_ci{
9038c2ecf20Sopenharmony_ci	struct atl1c_tpd_ring *tpd_ring = adapter->tpd_ring;
9048c2ecf20Sopenharmony_ci	struct atl1c_rfd_ring *rfd_ring = &adapter->rfd_ring;
9058c2ecf20Sopenharmony_ci	struct atl1c_rrd_ring *rrd_ring = &adapter->rrd_ring;
9068c2ecf20Sopenharmony_ci	struct atl1c_buffer *buffer_info;
9078c2ecf20Sopenharmony_ci	int i, j;
9088c2ecf20Sopenharmony_ci
9098c2ecf20Sopenharmony_ci	for (i = 0; i < AT_MAX_TRANSMIT_QUEUE; i++) {
9108c2ecf20Sopenharmony_ci		tpd_ring[i].next_to_use = 0;
9118c2ecf20Sopenharmony_ci		atomic_set(&tpd_ring[i].next_to_clean, 0);
9128c2ecf20Sopenharmony_ci		buffer_info = tpd_ring[i].buffer_info;
9138c2ecf20Sopenharmony_ci		for (j = 0; j < tpd_ring->count; j++)
9148c2ecf20Sopenharmony_ci			ATL1C_SET_BUFFER_STATE(&buffer_info[i],
9158c2ecf20Sopenharmony_ci					ATL1C_BUFFER_FREE);
9168c2ecf20Sopenharmony_ci	}
9178c2ecf20Sopenharmony_ci	rfd_ring->next_to_use = 0;
9188c2ecf20Sopenharmony_ci	rfd_ring->next_to_clean = 0;
9198c2ecf20Sopenharmony_ci	rrd_ring->next_to_use = 0;
9208c2ecf20Sopenharmony_ci	rrd_ring->next_to_clean = 0;
9218c2ecf20Sopenharmony_ci	for (j = 0; j < rfd_ring->count; j++) {
9228c2ecf20Sopenharmony_ci		buffer_info = &rfd_ring->buffer_info[j];
9238c2ecf20Sopenharmony_ci		ATL1C_SET_BUFFER_STATE(buffer_info, ATL1C_BUFFER_FREE);
9248c2ecf20Sopenharmony_ci	}
9258c2ecf20Sopenharmony_ci}
9268c2ecf20Sopenharmony_ci
9278c2ecf20Sopenharmony_ci/**
9288c2ecf20Sopenharmony_ci * atl1c_free_ring_resources - Free Tx / RX descriptor Resources
9298c2ecf20Sopenharmony_ci * @adapter: board private structure
9308c2ecf20Sopenharmony_ci *
9318c2ecf20Sopenharmony_ci * Free all transmit software resources
9328c2ecf20Sopenharmony_ci */
9338c2ecf20Sopenharmony_cistatic void atl1c_free_ring_resources(struct atl1c_adapter *adapter)
9348c2ecf20Sopenharmony_ci{
9358c2ecf20Sopenharmony_ci	struct pci_dev *pdev = adapter->pdev;
9368c2ecf20Sopenharmony_ci
9378c2ecf20Sopenharmony_ci	dma_free_coherent(&pdev->dev, adapter->ring_header.size,
9388c2ecf20Sopenharmony_ci			  adapter->ring_header.desc, adapter->ring_header.dma);
9398c2ecf20Sopenharmony_ci	adapter->ring_header.desc = NULL;
9408c2ecf20Sopenharmony_ci
9418c2ecf20Sopenharmony_ci	/* Note: just free tdp_ring.buffer_info,
9428c2ecf20Sopenharmony_ci	*  it contain rfd_ring.buffer_info, do not double free */
9438c2ecf20Sopenharmony_ci	if (adapter->tpd_ring[0].buffer_info) {
9448c2ecf20Sopenharmony_ci		kfree(adapter->tpd_ring[0].buffer_info);
9458c2ecf20Sopenharmony_ci		adapter->tpd_ring[0].buffer_info = NULL;
9468c2ecf20Sopenharmony_ci	}
9478c2ecf20Sopenharmony_ci	if (adapter->rx_page) {
9488c2ecf20Sopenharmony_ci		put_page(adapter->rx_page);
9498c2ecf20Sopenharmony_ci		adapter->rx_page = NULL;
9508c2ecf20Sopenharmony_ci	}
9518c2ecf20Sopenharmony_ci}
9528c2ecf20Sopenharmony_ci
9538c2ecf20Sopenharmony_ci/**
9548c2ecf20Sopenharmony_ci * atl1c_setup_mem_resources - allocate Tx / RX descriptor resources
9558c2ecf20Sopenharmony_ci * @adapter: board private structure
9568c2ecf20Sopenharmony_ci *
9578c2ecf20Sopenharmony_ci * Return 0 on success, negative on failure
9588c2ecf20Sopenharmony_ci */
9598c2ecf20Sopenharmony_cistatic int atl1c_setup_ring_resources(struct atl1c_adapter *adapter)
9608c2ecf20Sopenharmony_ci{
9618c2ecf20Sopenharmony_ci	struct pci_dev *pdev = adapter->pdev;
9628c2ecf20Sopenharmony_ci	struct atl1c_tpd_ring *tpd_ring = adapter->tpd_ring;
9638c2ecf20Sopenharmony_ci	struct atl1c_rfd_ring *rfd_ring = &adapter->rfd_ring;
9648c2ecf20Sopenharmony_ci	struct atl1c_rrd_ring *rrd_ring = &adapter->rrd_ring;
9658c2ecf20Sopenharmony_ci	struct atl1c_ring_header *ring_header = &adapter->ring_header;
9668c2ecf20Sopenharmony_ci	int size;
9678c2ecf20Sopenharmony_ci	int i;
9688c2ecf20Sopenharmony_ci	int count = 0;
9698c2ecf20Sopenharmony_ci	int rx_desc_count = 0;
9708c2ecf20Sopenharmony_ci	u32 offset = 0;
9718c2ecf20Sopenharmony_ci
9728c2ecf20Sopenharmony_ci	rrd_ring->count = rfd_ring->count;
9738c2ecf20Sopenharmony_ci	for (i = 1; i < AT_MAX_TRANSMIT_QUEUE; i++)
9748c2ecf20Sopenharmony_ci		tpd_ring[i].count = tpd_ring[0].count;
9758c2ecf20Sopenharmony_ci
9768c2ecf20Sopenharmony_ci	/* 2 tpd queue, one high priority queue,
9778c2ecf20Sopenharmony_ci	 * another normal priority queue */
9788c2ecf20Sopenharmony_ci	size = sizeof(struct atl1c_buffer) * (tpd_ring->count * 2 +
9798c2ecf20Sopenharmony_ci		rfd_ring->count);
9808c2ecf20Sopenharmony_ci	tpd_ring->buffer_info = kzalloc(size, GFP_KERNEL);
9818c2ecf20Sopenharmony_ci	if (unlikely(!tpd_ring->buffer_info))
9828c2ecf20Sopenharmony_ci		goto err_nomem;
9838c2ecf20Sopenharmony_ci
9848c2ecf20Sopenharmony_ci	for (i = 0; i < AT_MAX_TRANSMIT_QUEUE; i++) {
9858c2ecf20Sopenharmony_ci		tpd_ring[i].buffer_info =
9868c2ecf20Sopenharmony_ci			(tpd_ring->buffer_info + count);
9878c2ecf20Sopenharmony_ci		count += tpd_ring[i].count;
9888c2ecf20Sopenharmony_ci	}
9898c2ecf20Sopenharmony_ci
9908c2ecf20Sopenharmony_ci	rfd_ring->buffer_info =
9918c2ecf20Sopenharmony_ci		(tpd_ring->buffer_info + count);
9928c2ecf20Sopenharmony_ci	count += rfd_ring->count;
9938c2ecf20Sopenharmony_ci	rx_desc_count += rfd_ring->count;
9948c2ecf20Sopenharmony_ci
9958c2ecf20Sopenharmony_ci	/*
9968c2ecf20Sopenharmony_ci	 * real ring DMA buffer
9978c2ecf20Sopenharmony_ci	 * each ring/block may need up to 8 bytes for alignment, hence the
9988c2ecf20Sopenharmony_ci	 * additional bytes tacked onto the end.
9998c2ecf20Sopenharmony_ci	 */
10008c2ecf20Sopenharmony_ci	ring_header->size = size =
10018c2ecf20Sopenharmony_ci		sizeof(struct atl1c_tpd_desc) * tpd_ring->count * 2 +
10028c2ecf20Sopenharmony_ci		sizeof(struct atl1c_rx_free_desc) * rx_desc_count +
10038c2ecf20Sopenharmony_ci		sizeof(struct atl1c_recv_ret_status) * rx_desc_count +
10048c2ecf20Sopenharmony_ci		8 * 4;
10058c2ecf20Sopenharmony_ci
10068c2ecf20Sopenharmony_ci	ring_header->desc = dma_alloc_coherent(&pdev->dev, ring_header->size,
10078c2ecf20Sopenharmony_ci					       &ring_header->dma, GFP_KERNEL);
10088c2ecf20Sopenharmony_ci	if (unlikely(!ring_header->desc)) {
10098c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "could not get memory for DMA buffer\n");
10108c2ecf20Sopenharmony_ci		goto err_nomem;
10118c2ecf20Sopenharmony_ci	}
10128c2ecf20Sopenharmony_ci	/* init TPD ring */
10138c2ecf20Sopenharmony_ci
10148c2ecf20Sopenharmony_ci	tpd_ring[0].dma = roundup(ring_header->dma, 8);
10158c2ecf20Sopenharmony_ci	offset = tpd_ring[0].dma - ring_header->dma;
10168c2ecf20Sopenharmony_ci	for (i = 0; i < AT_MAX_TRANSMIT_QUEUE; i++) {
10178c2ecf20Sopenharmony_ci		tpd_ring[i].dma = ring_header->dma + offset;
10188c2ecf20Sopenharmony_ci		tpd_ring[i].desc = (u8 *) ring_header->desc + offset;
10198c2ecf20Sopenharmony_ci		tpd_ring[i].size =
10208c2ecf20Sopenharmony_ci			sizeof(struct atl1c_tpd_desc) * tpd_ring[i].count;
10218c2ecf20Sopenharmony_ci		offset += roundup(tpd_ring[i].size, 8);
10228c2ecf20Sopenharmony_ci	}
10238c2ecf20Sopenharmony_ci	/* init RFD ring */
10248c2ecf20Sopenharmony_ci	rfd_ring->dma = ring_header->dma + offset;
10258c2ecf20Sopenharmony_ci	rfd_ring->desc = (u8 *) ring_header->desc + offset;
10268c2ecf20Sopenharmony_ci	rfd_ring->size = sizeof(struct atl1c_rx_free_desc) * rfd_ring->count;
10278c2ecf20Sopenharmony_ci	offset += roundup(rfd_ring->size, 8);
10288c2ecf20Sopenharmony_ci
10298c2ecf20Sopenharmony_ci	/* init RRD ring */
10308c2ecf20Sopenharmony_ci	rrd_ring->dma = ring_header->dma + offset;
10318c2ecf20Sopenharmony_ci	rrd_ring->desc = (u8 *) ring_header->desc + offset;
10328c2ecf20Sopenharmony_ci	rrd_ring->size = sizeof(struct atl1c_recv_ret_status) *
10338c2ecf20Sopenharmony_ci		rrd_ring->count;
10348c2ecf20Sopenharmony_ci	offset += roundup(rrd_ring->size, 8);
10358c2ecf20Sopenharmony_ci
10368c2ecf20Sopenharmony_ci	return 0;
10378c2ecf20Sopenharmony_ci
10388c2ecf20Sopenharmony_cierr_nomem:
10398c2ecf20Sopenharmony_ci	kfree(tpd_ring->buffer_info);
10408c2ecf20Sopenharmony_ci	return -ENOMEM;
10418c2ecf20Sopenharmony_ci}
10428c2ecf20Sopenharmony_ci
10438c2ecf20Sopenharmony_cistatic void atl1c_configure_des_ring(struct atl1c_adapter *adapter)
10448c2ecf20Sopenharmony_ci{
10458c2ecf20Sopenharmony_ci	struct atl1c_hw *hw = &adapter->hw;
10468c2ecf20Sopenharmony_ci	struct atl1c_rfd_ring *rfd_ring = &adapter->rfd_ring;
10478c2ecf20Sopenharmony_ci	struct atl1c_rrd_ring *rrd_ring = &adapter->rrd_ring;
10488c2ecf20Sopenharmony_ci	struct atl1c_tpd_ring *tpd_ring = (struct atl1c_tpd_ring *)
10498c2ecf20Sopenharmony_ci				adapter->tpd_ring;
10508c2ecf20Sopenharmony_ci
10518c2ecf20Sopenharmony_ci	/* TPD */
10528c2ecf20Sopenharmony_ci	AT_WRITE_REG(hw, REG_TX_BASE_ADDR_HI,
10538c2ecf20Sopenharmony_ci			(u32)((tpd_ring[atl1c_trans_normal].dma &
10548c2ecf20Sopenharmony_ci				AT_DMA_HI_ADDR_MASK) >> 32));
10558c2ecf20Sopenharmony_ci	/* just enable normal priority TX queue */
10568c2ecf20Sopenharmony_ci	AT_WRITE_REG(hw, REG_TPD_PRI0_ADDR_LO,
10578c2ecf20Sopenharmony_ci			(u32)(tpd_ring[atl1c_trans_normal].dma &
10588c2ecf20Sopenharmony_ci				AT_DMA_LO_ADDR_MASK));
10598c2ecf20Sopenharmony_ci	AT_WRITE_REG(hw, REG_TPD_PRI1_ADDR_LO,
10608c2ecf20Sopenharmony_ci			(u32)(tpd_ring[atl1c_trans_high].dma &
10618c2ecf20Sopenharmony_ci				AT_DMA_LO_ADDR_MASK));
10628c2ecf20Sopenharmony_ci	AT_WRITE_REG(hw, REG_TPD_RING_SIZE,
10638c2ecf20Sopenharmony_ci			(u32)(tpd_ring[0].count & TPD_RING_SIZE_MASK));
10648c2ecf20Sopenharmony_ci
10658c2ecf20Sopenharmony_ci
10668c2ecf20Sopenharmony_ci	/* RFD */
10678c2ecf20Sopenharmony_ci	AT_WRITE_REG(hw, REG_RX_BASE_ADDR_HI,
10688c2ecf20Sopenharmony_ci			(u32)((rfd_ring->dma & AT_DMA_HI_ADDR_MASK) >> 32));
10698c2ecf20Sopenharmony_ci	AT_WRITE_REG(hw, REG_RFD0_HEAD_ADDR_LO,
10708c2ecf20Sopenharmony_ci			(u32)(rfd_ring->dma & AT_DMA_LO_ADDR_MASK));
10718c2ecf20Sopenharmony_ci
10728c2ecf20Sopenharmony_ci	AT_WRITE_REG(hw, REG_RFD_RING_SIZE,
10738c2ecf20Sopenharmony_ci			rfd_ring->count & RFD_RING_SIZE_MASK);
10748c2ecf20Sopenharmony_ci	AT_WRITE_REG(hw, REG_RX_BUF_SIZE,
10758c2ecf20Sopenharmony_ci			adapter->rx_buffer_len & RX_BUF_SIZE_MASK);
10768c2ecf20Sopenharmony_ci
10778c2ecf20Sopenharmony_ci	/* RRD */
10788c2ecf20Sopenharmony_ci	AT_WRITE_REG(hw, REG_RRD0_HEAD_ADDR_LO,
10798c2ecf20Sopenharmony_ci			(u32)(rrd_ring->dma & AT_DMA_LO_ADDR_MASK));
10808c2ecf20Sopenharmony_ci	AT_WRITE_REG(hw, REG_RRD_RING_SIZE,
10818c2ecf20Sopenharmony_ci			(rrd_ring->count & RRD_RING_SIZE_MASK));
10828c2ecf20Sopenharmony_ci
10838c2ecf20Sopenharmony_ci	if (hw->nic_type == athr_l2c_b) {
10848c2ecf20Sopenharmony_ci		AT_WRITE_REG(hw, REG_SRAM_RXF_LEN, 0x02a0L);
10858c2ecf20Sopenharmony_ci		AT_WRITE_REG(hw, REG_SRAM_TXF_LEN, 0x0100L);
10868c2ecf20Sopenharmony_ci		AT_WRITE_REG(hw, REG_SRAM_RXF_ADDR, 0x029f0000L);
10878c2ecf20Sopenharmony_ci		AT_WRITE_REG(hw, REG_SRAM_RFD0_INFO, 0x02bf02a0L);
10888c2ecf20Sopenharmony_ci		AT_WRITE_REG(hw, REG_SRAM_TXF_ADDR, 0x03bf02c0L);
10898c2ecf20Sopenharmony_ci		AT_WRITE_REG(hw, REG_SRAM_TRD_ADDR, 0x03df03c0L);
10908c2ecf20Sopenharmony_ci		AT_WRITE_REG(hw, REG_TXF_WATER_MARK, 0);	/* TX watermark, to enter l1 state.*/
10918c2ecf20Sopenharmony_ci		AT_WRITE_REG(hw, REG_RXD_DMA_CTRL, 0);		/* RXD threshold.*/
10928c2ecf20Sopenharmony_ci	}
10938c2ecf20Sopenharmony_ci	/* Load all of base address above */
10948c2ecf20Sopenharmony_ci	AT_WRITE_REG(hw, REG_LOAD_PTR, 1);
10958c2ecf20Sopenharmony_ci}
10968c2ecf20Sopenharmony_ci
10978c2ecf20Sopenharmony_cistatic void atl1c_configure_tx(struct atl1c_adapter *adapter)
10988c2ecf20Sopenharmony_ci{
10998c2ecf20Sopenharmony_ci	struct atl1c_hw *hw = &adapter->hw;
11008c2ecf20Sopenharmony_ci	int max_pay_load;
11018c2ecf20Sopenharmony_ci	u16 tx_offload_thresh;
11028c2ecf20Sopenharmony_ci	u32 txq_ctrl_data;
11038c2ecf20Sopenharmony_ci
11048c2ecf20Sopenharmony_ci	tx_offload_thresh = MAX_TSO_FRAME_SIZE;
11058c2ecf20Sopenharmony_ci	AT_WRITE_REG(hw, REG_TX_TSO_OFFLOAD_THRESH,
11068c2ecf20Sopenharmony_ci		(tx_offload_thresh >> 3) & TX_TSO_OFFLOAD_THRESH_MASK);
11078c2ecf20Sopenharmony_ci	max_pay_load = pcie_get_readrq(adapter->pdev) >> 8;
11088c2ecf20Sopenharmony_ci	hw->dmar_block = min_t(u32, max_pay_load, hw->dmar_block);
11098c2ecf20Sopenharmony_ci	/*
11108c2ecf20Sopenharmony_ci	 * if BIOS had changed the dam-read-max-length to an invalid value,
11118c2ecf20Sopenharmony_ci	 * restore it to default value
11128c2ecf20Sopenharmony_ci	 */
11138c2ecf20Sopenharmony_ci	if (hw->dmar_block < DEVICE_CTRL_MAXRRS_MIN) {
11148c2ecf20Sopenharmony_ci		pcie_set_readrq(adapter->pdev, 128 << DEVICE_CTRL_MAXRRS_MIN);
11158c2ecf20Sopenharmony_ci		hw->dmar_block = DEVICE_CTRL_MAXRRS_MIN;
11168c2ecf20Sopenharmony_ci	}
11178c2ecf20Sopenharmony_ci	txq_ctrl_data =
11188c2ecf20Sopenharmony_ci		hw->nic_type == athr_l2c_b || hw->nic_type == athr_l2c_b2 ?
11198c2ecf20Sopenharmony_ci		L2CB_TXQ_CFGV : L1C_TXQ_CFGV;
11208c2ecf20Sopenharmony_ci
11218c2ecf20Sopenharmony_ci	AT_WRITE_REG(hw, REG_TXQ_CTRL, txq_ctrl_data);
11228c2ecf20Sopenharmony_ci}
11238c2ecf20Sopenharmony_ci
11248c2ecf20Sopenharmony_cistatic void atl1c_configure_rx(struct atl1c_adapter *adapter)
11258c2ecf20Sopenharmony_ci{
11268c2ecf20Sopenharmony_ci	struct atl1c_hw *hw = &adapter->hw;
11278c2ecf20Sopenharmony_ci	u32 rxq_ctrl_data;
11288c2ecf20Sopenharmony_ci
11298c2ecf20Sopenharmony_ci	rxq_ctrl_data = (hw->rfd_burst & RXQ_RFD_BURST_NUM_MASK) <<
11308c2ecf20Sopenharmony_ci			RXQ_RFD_BURST_NUM_SHIFT;
11318c2ecf20Sopenharmony_ci
11328c2ecf20Sopenharmony_ci	if (hw->ctrl_flags & ATL1C_RX_IPV6_CHKSUM)
11338c2ecf20Sopenharmony_ci		rxq_ctrl_data |= IPV6_CHKSUM_CTRL_EN;
11348c2ecf20Sopenharmony_ci
11358c2ecf20Sopenharmony_ci	/* aspm for gigabit */
11368c2ecf20Sopenharmony_ci	if (hw->nic_type != athr_l1d_2 && (hw->device_id & 1) != 0)
11378c2ecf20Sopenharmony_ci		rxq_ctrl_data = FIELD_SETX(rxq_ctrl_data, ASPM_THRUPUT_LIMIT,
11388c2ecf20Sopenharmony_ci			ASPM_THRUPUT_LIMIT_100M);
11398c2ecf20Sopenharmony_ci
11408c2ecf20Sopenharmony_ci	AT_WRITE_REG(hw, REG_RXQ_CTRL, rxq_ctrl_data);
11418c2ecf20Sopenharmony_ci}
11428c2ecf20Sopenharmony_ci
11438c2ecf20Sopenharmony_cistatic void atl1c_configure_dma(struct atl1c_adapter *adapter)
11448c2ecf20Sopenharmony_ci{
11458c2ecf20Sopenharmony_ci	struct atl1c_hw *hw = &adapter->hw;
11468c2ecf20Sopenharmony_ci	u32 dma_ctrl_data;
11478c2ecf20Sopenharmony_ci
11488c2ecf20Sopenharmony_ci	dma_ctrl_data = FIELDX(DMA_CTRL_RORDER_MODE, DMA_CTRL_RORDER_MODE_OUT) |
11498c2ecf20Sopenharmony_ci		DMA_CTRL_RREQ_PRI_DATA |
11508c2ecf20Sopenharmony_ci		FIELDX(DMA_CTRL_RREQ_BLEN, hw->dmar_block) |
11518c2ecf20Sopenharmony_ci		FIELDX(DMA_CTRL_WDLY_CNT, DMA_CTRL_WDLY_CNT_DEF) |
11528c2ecf20Sopenharmony_ci		FIELDX(DMA_CTRL_RDLY_CNT, DMA_CTRL_RDLY_CNT_DEF);
11538c2ecf20Sopenharmony_ci
11548c2ecf20Sopenharmony_ci	AT_WRITE_REG(hw, REG_DMA_CTRL, dma_ctrl_data);
11558c2ecf20Sopenharmony_ci}
11568c2ecf20Sopenharmony_ci
11578c2ecf20Sopenharmony_ci/*
11588c2ecf20Sopenharmony_ci * Stop the mac, transmit and receive units
11598c2ecf20Sopenharmony_ci * hw - Struct containing variables accessed by shared code
11608c2ecf20Sopenharmony_ci * return : 0  or  idle status (if error)
11618c2ecf20Sopenharmony_ci */
11628c2ecf20Sopenharmony_cistatic int atl1c_stop_mac(struct atl1c_hw *hw)
11638c2ecf20Sopenharmony_ci{
11648c2ecf20Sopenharmony_ci	u32 data;
11658c2ecf20Sopenharmony_ci
11668c2ecf20Sopenharmony_ci	AT_READ_REG(hw, REG_RXQ_CTRL, &data);
11678c2ecf20Sopenharmony_ci	data &= ~RXQ_CTRL_EN;
11688c2ecf20Sopenharmony_ci	AT_WRITE_REG(hw, REG_RXQ_CTRL, data);
11698c2ecf20Sopenharmony_ci
11708c2ecf20Sopenharmony_ci	AT_READ_REG(hw, REG_TXQ_CTRL, &data);
11718c2ecf20Sopenharmony_ci	data &= ~TXQ_CTRL_EN;
11728c2ecf20Sopenharmony_ci	AT_WRITE_REG(hw, REG_TXQ_CTRL, data);
11738c2ecf20Sopenharmony_ci
11748c2ecf20Sopenharmony_ci	atl1c_wait_until_idle(hw, IDLE_STATUS_RXQ_BUSY | IDLE_STATUS_TXQ_BUSY);
11758c2ecf20Sopenharmony_ci
11768c2ecf20Sopenharmony_ci	AT_READ_REG(hw, REG_MAC_CTRL, &data);
11778c2ecf20Sopenharmony_ci	data &= ~(MAC_CTRL_TX_EN | MAC_CTRL_RX_EN);
11788c2ecf20Sopenharmony_ci	AT_WRITE_REG(hw, REG_MAC_CTRL, data);
11798c2ecf20Sopenharmony_ci
11808c2ecf20Sopenharmony_ci	return (int)atl1c_wait_until_idle(hw,
11818c2ecf20Sopenharmony_ci		IDLE_STATUS_TXMAC_BUSY | IDLE_STATUS_RXMAC_BUSY);
11828c2ecf20Sopenharmony_ci}
11838c2ecf20Sopenharmony_ci
11848c2ecf20Sopenharmony_cistatic void atl1c_start_mac(struct atl1c_adapter *adapter)
11858c2ecf20Sopenharmony_ci{
11868c2ecf20Sopenharmony_ci	struct atl1c_hw *hw = &adapter->hw;
11878c2ecf20Sopenharmony_ci	u32 mac, txq, rxq;
11888c2ecf20Sopenharmony_ci
11898c2ecf20Sopenharmony_ci	hw->mac_duplex = adapter->link_duplex == FULL_DUPLEX;
11908c2ecf20Sopenharmony_ci	hw->mac_speed = adapter->link_speed == SPEED_1000 ?
11918c2ecf20Sopenharmony_ci		atl1c_mac_speed_1000 : atl1c_mac_speed_10_100;
11928c2ecf20Sopenharmony_ci
11938c2ecf20Sopenharmony_ci	AT_READ_REG(hw, REG_TXQ_CTRL, &txq);
11948c2ecf20Sopenharmony_ci	AT_READ_REG(hw, REG_RXQ_CTRL, &rxq);
11958c2ecf20Sopenharmony_ci	AT_READ_REG(hw, REG_MAC_CTRL, &mac);
11968c2ecf20Sopenharmony_ci
11978c2ecf20Sopenharmony_ci	txq |= TXQ_CTRL_EN;
11988c2ecf20Sopenharmony_ci	rxq |= RXQ_CTRL_EN;
11998c2ecf20Sopenharmony_ci	mac |= MAC_CTRL_TX_EN | MAC_CTRL_TX_FLOW |
12008c2ecf20Sopenharmony_ci	       MAC_CTRL_RX_EN | MAC_CTRL_RX_FLOW |
12018c2ecf20Sopenharmony_ci	       MAC_CTRL_ADD_CRC | MAC_CTRL_PAD |
12028c2ecf20Sopenharmony_ci	       MAC_CTRL_BC_EN | MAC_CTRL_SINGLE_PAUSE_EN |
12038c2ecf20Sopenharmony_ci	       MAC_CTRL_HASH_ALG_CRC32;
12048c2ecf20Sopenharmony_ci	if (hw->mac_duplex)
12058c2ecf20Sopenharmony_ci		mac |= MAC_CTRL_DUPLX;
12068c2ecf20Sopenharmony_ci	else
12078c2ecf20Sopenharmony_ci		mac &= ~MAC_CTRL_DUPLX;
12088c2ecf20Sopenharmony_ci	mac = FIELD_SETX(mac, MAC_CTRL_SPEED, hw->mac_speed);
12098c2ecf20Sopenharmony_ci	mac = FIELD_SETX(mac, MAC_CTRL_PRMLEN, hw->preamble_len);
12108c2ecf20Sopenharmony_ci
12118c2ecf20Sopenharmony_ci	AT_WRITE_REG(hw, REG_TXQ_CTRL, txq);
12128c2ecf20Sopenharmony_ci	AT_WRITE_REG(hw, REG_RXQ_CTRL, rxq);
12138c2ecf20Sopenharmony_ci	AT_WRITE_REG(hw, REG_MAC_CTRL, mac);
12148c2ecf20Sopenharmony_ci}
12158c2ecf20Sopenharmony_ci
12168c2ecf20Sopenharmony_ci/*
12178c2ecf20Sopenharmony_ci * Reset the transmit and receive units; mask and clear all interrupts.
12188c2ecf20Sopenharmony_ci * hw - Struct containing variables accessed by shared code
12198c2ecf20Sopenharmony_ci * return : 0  or  idle status (if error)
12208c2ecf20Sopenharmony_ci */
12218c2ecf20Sopenharmony_cistatic int atl1c_reset_mac(struct atl1c_hw *hw)
12228c2ecf20Sopenharmony_ci{
12238c2ecf20Sopenharmony_ci	struct atl1c_adapter *adapter = hw->adapter;
12248c2ecf20Sopenharmony_ci	struct pci_dev *pdev = adapter->pdev;
12258c2ecf20Sopenharmony_ci	u32 ctrl_data = 0;
12268c2ecf20Sopenharmony_ci
12278c2ecf20Sopenharmony_ci	atl1c_stop_mac(hw);
12288c2ecf20Sopenharmony_ci	/*
12298c2ecf20Sopenharmony_ci	 * Issue Soft Reset to the MAC.  This will reset the chip's
12308c2ecf20Sopenharmony_ci	 * transmit, receive, DMA.  It will not effect
12318c2ecf20Sopenharmony_ci	 * the current PCI configuration.  The global reset bit is self-
12328c2ecf20Sopenharmony_ci	 * clearing, and should clear within a microsecond.
12338c2ecf20Sopenharmony_ci	 */
12348c2ecf20Sopenharmony_ci	AT_READ_REG(hw, REG_MASTER_CTRL, &ctrl_data);
12358c2ecf20Sopenharmony_ci	ctrl_data |= MASTER_CTRL_OOB_DIS;
12368c2ecf20Sopenharmony_ci	AT_WRITE_REG(hw, REG_MASTER_CTRL, ctrl_data | MASTER_CTRL_SOFT_RST);
12378c2ecf20Sopenharmony_ci
12388c2ecf20Sopenharmony_ci	AT_WRITE_FLUSH(hw);
12398c2ecf20Sopenharmony_ci	msleep(10);
12408c2ecf20Sopenharmony_ci	/* Wait at least 10ms for All module to be Idle */
12418c2ecf20Sopenharmony_ci
12428c2ecf20Sopenharmony_ci	if (atl1c_wait_until_idle(hw, IDLE_STATUS_MASK)) {
12438c2ecf20Sopenharmony_ci		dev_err(&pdev->dev,
12448c2ecf20Sopenharmony_ci			"MAC state machine can't be idle since"
12458c2ecf20Sopenharmony_ci			" disabled for 10ms second\n");
12468c2ecf20Sopenharmony_ci		return -1;
12478c2ecf20Sopenharmony_ci	}
12488c2ecf20Sopenharmony_ci	AT_WRITE_REG(hw, REG_MASTER_CTRL, ctrl_data);
12498c2ecf20Sopenharmony_ci
12508c2ecf20Sopenharmony_ci	/* driver control speed/duplex */
12518c2ecf20Sopenharmony_ci	AT_READ_REG(hw, REG_MAC_CTRL, &ctrl_data);
12528c2ecf20Sopenharmony_ci	AT_WRITE_REG(hw, REG_MAC_CTRL, ctrl_data | MAC_CTRL_SPEED_MODE_SW);
12538c2ecf20Sopenharmony_ci
12548c2ecf20Sopenharmony_ci	/* clk switch setting */
12558c2ecf20Sopenharmony_ci	AT_READ_REG(hw, REG_SERDES, &ctrl_data);
12568c2ecf20Sopenharmony_ci	switch (hw->nic_type) {
12578c2ecf20Sopenharmony_ci	case athr_l2c_b:
12588c2ecf20Sopenharmony_ci		ctrl_data &= ~(SERDES_PHY_CLK_SLOWDOWN |
12598c2ecf20Sopenharmony_ci				SERDES_MAC_CLK_SLOWDOWN);
12608c2ecf20Sopenharmony_ci		AT_WRITE_REG(hw, REG_SERDES, ctrl_data);
12618c2ecf20Sopenharmony_ci		break;
12628c2ecf20Sopenharmony_ci	case athr_l2c_b2:
12638c2ecf20Sopenharmony_ci	case athr_l1d_2:
12648c2ecf20Sopenharmony_ci		ctrl_data |= SERDES_PHY_CLK_SLOWDOWN | SERDES_MAC_CLK_SLOWDOWN;
12658c2ecf20Sopenharmony_ci		AT_WRITE_REG(hw, REG_SERDES, ctrl_data);
12668c2ecf20Sopenharmony_ci		break;
12678c2ecf20Sopenharmony_ci	default:
12688c2ecf20Sopenharmony_ci		break;
12698c2ecf20Sopenharmony_ci	}
12708c2ecf20Sopenharmony_ci
12718c2ecf20Sopenharmony_ci	return 0;
12728c2ecf20Sopenharmony_ci}
12738c2ecf20Sopenharmony_ci
12748c2ecf20Sopenharmony_cistatic void atl1c_disable_l0s_l1(struct atl1c_hw *hw)
12758c2ecf20Sopenharmony_ci{
12768c2ecf20Sopenharmony_ci	u16 ctrl_flags = hw->ctrl_flags;
12778c2ecf20Sopenharmony_ci
12788c2ecf20Sopenharmony_ci	hw->ctrl_flags &= ~(ATL1C_ASPM_L0S_SUPPORT | ATL1C_ASPM_L1_SUPPORT);
12798c2ecf20Sopenharmony_ci	atl1c_set_aspm(hw, SPEED_0);
12808c2ecf20Sopenharmony_ci	hw->ctrl_flags = ctrl_flags;
12818c2ecf20Sopenharmony_ci}
12828c2ecf20Sopenharmony_ci
12838c2ecf20Sopenharmony_ci/*
12848c2ecf20Sopenharmony_ci * Set ASPM state.
12858c2ecf20Sopenharmony_ci * Enable/disable L0s/L1 depend on link state.
12868c2ecf20Sopenharmony_ci */
12878c2ecf20Sopenharmony_cistatic void atl1c_set_aspm(struct atl1c_hw *hw, u16 link_speed)
12888c2ecf20Sopenharmony_ci{
12898c2ecf20Sopenharmony_ci	u32 pm_ctrl_data;
12908c2ecf20Sopenharmony_ci	u32 link_l1_timer;
12918c2ecf20Sopenharmony_ci
12928c2ecf20Sopenharmony_ci	AT_READ_REG(hw, REG_PM_CTRL, &pm_ctrl_data);
12938c2ecf20Sopenharmony_ci	pm_ctrl_data &= ~(PM_CTRL_ASPM_L1_EN |
12948c2ecf20Sopenharmony_ci			  PM_CTRL_ASPM_L0S_EN |
12958c2ecf20Sopenharmony_ci			  PM_CTRL_MAC_ASPM_CHK);
12968c2ecf20Sopenharmony_ci	/* L1 timer */
12978c2ecf20Sopenharmony_ci	if (hw->nic_type == athr_l2c_b2 || hw->nic_type == athr_l1d_2) {
12988c2ecf20Sopenharmony_ci		pm_ctrl_data &= ~PMCTRL_TXL1_AFTER_L0S;
12998c2ecf20Sopenharmony_ci		link_l1_timer =
13008c2ecf20Sopenharmony_ci			link_speed == SPEED_1000 || link_speed == SPEED_100 ?
13018c2ecf20Sopenharmony_ci			L1D_PMCTRL_L1_ENTRY_TM_16US : 1;
13028c2ecf20Sopenharmony_ci		pm_ctrl_data = FIELD_SETX(pm_ctrl_data,
13038c2ecf20Sopenharmony_ci			L1D_PMCTRL_L1_ENTRY_TM, link_l1_timer);
13048c2ecf20Sopenharmony_ci	} else {
13058c2ecf20Sopenharmony_ci		link_l1_timer = hw->nic_type == athr_l2c_b ?
13068c2ecf20Sopenharmony_ci			L2CB1_PM_CTRL_L1_ENTRY_TM : L1C_PM_CTRL_L1_ENTRY_TM;
13078c2ecf20Sopenharmony_ci		if (link_speed != SPEED_1000 && link_speed != SPEED_100)
13088c2ecf20Sopenharmony_ci			link_l1_timer = 1;
13098c2ecf20Sopenharmony_ci		pm_ctrl_data = FIELD_SETX(pm_ctrl_data,
13108c2ecf20Sopenharmony_ci			PM_CTRL_L1_ENTRY_TIMER, link_l1_timer);
13118c2ecf20Sopenharmony_ci	}
13128c2ecf20Sopenharmony_ci
13138c2ecf20Sopenharmony_ci	/* L0S/L1 enable */
13148c2ecf20Sopenharmony_ci	if ((hw->ctrl_flags & ATL1C_ASPM_L0S_SUPPORT) && link_speed != SPEED_0)
13158c2ecf20Sopenharmony_ci		pm_ctrl_data |= PM_CTRL_ASPM_L0S_EN | PM_CTRL_MAC_ASPM_CHK;
13168c2ecf20Sopenharmony_ci	if (hw->ctrl_flags & ATL1C_ASPM_L1_SUPPORT)
13178c2ecf20Sopenharmony_ci		pm_ctrl_data |= PM_CTRL_ASPM_L1_EN | PM_CTRL_MAC_ASPM_CHK;
13188c2ecf20Sopenharmony_ci
13198c2ecf20Sopenharmony_ci	/* l2cb & l1d & l2cb2 & l1d2 */
13208c2ecf20Sopenharmony_ci	if (hw->nic_type == athr_l2c_b || hw->nic_type == athr_l1d ||
13218c2ecf20Sopenharmony_ci	    hw->nic_type == athr_l2c_b2 || hw->nic_type == athr_l1d_2) {
13228c2ecf20Sopenharmony_ci		pm_ctrl_data = FIELD_SETX(pm_ctrl_data,
13238c2ecf20Sopenharmony_ci			PM_CTRL_PM_REQ_TIMER, PM_CTRL_PM_REQ_TO_DEF);
13248c2ecf20Sopenharmony_ci		pm_ctrl_data |= PM_CTRL_RCVR_WT_TIMER |
13258c2ecf20Sopenharmony_ci				PM_CTRL_SERDES_PD_EX_L1 |
13268c2ecf20Sopenharmony_ci				PM_CTRL_CLK_SWH_L1;
13278c2ecf20Sopenharmony_ci		pm_ctrl_data &= ~(PM_CTRL_SERDES_L1_EN |
13288c2ecf20Sopenharmony_ci				  PM_CTRL_SERDES_PLL_L1_EN |
13298c2ecf20Sopenharmony_ci				  PM_CTRL_SERDES_BUFS_RX_L1_EN |
13308c2ecf20Sopenharmony_ci				  PM_CTRL_SA_DLY_EN |
13318c2ecf20Sopenharmony_ci				  PM_CTRL_HOTRST);
13328c2ecf20Sopenharmony_ci		/* disable l0s if link down or l2cb */
13338c2ecf20Sopenharmony_ci		if (link_speed == SPEED_0 || hw->nic_type == athr_l2c_b)
13348c2ecf20Sopenharmony_ci			pm_ctrl_data &= ~PM_CTRL_ASPM_L0S_EN;
13358c2ecf20Sopenharmony_ci	} else { /* l1c */
13368c2ecf20Sopenharmony_ci		pm_ctrl_data =
13378c2ecf20Sopenharmony_ci			FIELD_SETX(pm_ctrl_data, PM_CTRL_L1_ENTRY_TIMER, 0);
13388c2ecf20Sopenharmony_ci		if (link_speed != SPEED_0) {
13398c2ecf20Sopenharmony_ci			pm_ctrl_data |= PM_CTRL_SERDES_L1_EN |
13408c2ecf20Sopenharmony_ci					PM_CTRL_SERDES_PLL_L1_EN |
13418c2ecf20Sopenharmony_ci					PM_CTRL_SERDES_BUFS_RX_L1_EN;
13428c2ecf20Sopenharmony_ci			pm_ctrl_data &= ~(PM_CTRL_SERDES_PD_EX_L1 |
13438c2ecf20Sopenharmony_ci					  PM_CTRL_CLK_SWH_L1 |
13448c2ecf20Sopenharmony_ci					  PM_CTRL_ASPM_L0S_EN |
13458c2ecf20Sopenharmony_ci					  PM_CTRL_ASPM_L1_EN);
13468c2ecf20Sopenharmony_ci		} else { /* link down */
13478c2ecf20Sopenharmony_ci			pm_ctrl_data |= PM_CTRL_CLK_SWH_L1;
13488c2ecf20Sopenharmony_ci			pm_ctrl_data &= ~(PM_CTRL_SERDES_L1_EN |
13498c2ecf20Sopenharmony_ci					  PM_CTRL_SERDES_PLL_L1_EN |
13508c2ecf20Sopenharmony_ci					  PM_CTRL_SERDES_BUFS_RX_L1_EN |
13518c2ecf20Sopenharmony_ci					  PM_CTRL_ASPM_L0S_EN);
13528c2ecf20Sopenharmony_ci		}
13538c2ecf20Sopenharmony_ci	}
13548c2ecf20Sopenharmony_ci	AT_WRITE_REG(hw, REG_PM_CTRL, pm_ctrl_data);
13558c2ecf20Sopenharmony_ci
13568c2ecf20Sopenharmony_ci	return;
13578c2ecf20Sopenharmony_ci}
13588c2ecf20Sopenharmony_ci
13598c2ecf20Sopenharmony_ci/**
13608c2ecf20Sopenharmony_ci * atl1c_configure - Configure Transmit&Receive Unit after Reset
13618c2ecf20Sopenharmony_ci * @adapter: board private structure
13628c2ecf20Sopenharmony_ci *
13638c2ecf20Sopenharmony_ci * Configure the Tx /Rx unit of the MAC after a reset.
13648c2ecf20Sopenharmony_ci */
13658c2ecf20Sopenharmony_cistatic int atl1c_configure_mac(struct atl1c_adapter *adapter)
13668c2ecf20Sopenharmony_ci{
13678c2ecf20Sopenharmony_ci	struct atl1c_hw *hw = &adapter->hw;
13688c2ecf20Sopenharmony_ci	u32 master_ctrl_data = 0;
13698c2ecf20Sopenharmony_ci	u32 intr_modrt_data;
13708c2ecf20Sopenharmony_ci	u32 data;
13718c2ecf20Sopenharmony_ci
13728c2ecf20Sopenharmony_ci	AT_READ_REG(hw, REG_MASTER_CTRL, &master_ctrl_data);
13738c2ecf20Sopenharmony_ci	master_ctrl_data &= ~(MASTER_CTRL_TX_ITIMER_EN |
13748c2ecf20Sopenharmony_ci			      MASTER_CTRL_RX_ITIMER_EN |
13758c2ecf20Sopenharmony_ci			      MASTER_CTRL_INT_RDCLR);
13768c2ecf20Sopenharmony_ci	/* clear interrupt status */
13778c2ecf20Sopenharmony_ci	AT_WRITE_REG(hw, REG_ISR, 0xFFFFFFFF);
13788c2ecf20Sopenharmony_ci	/*  Clear any WOL status */
13798c2ecf20Sopenharmony_ci	AT_WRITE_REG(hw, REG_WOL_CTRL, 0);
13808c2ecf20Sopenharmony_ci	/* set Interrupt Clear Timer
13818c2ecf20Sopenharmony_ci	 * HW will enable self to assert interrupt event to system after
13828c2ecf20Sopenharmony_ci	 * waiting x-time for software to notify it accept interrupt.
13838c2ecf20Sopenharmony_ci	 */
13848c2ecf20Sopenharmony_ci
13858c2ecf20Sopenharmony_ci	data = CLK_GATING_EN_ALL;
13868c2ecf20Sopenharmony_ci	if (hw->ctrl_flags & ATL1C_CLK_GATING_EN) {
13878c2ecf20Sopenharmony_ci		if (hw->nic_type == athr_l2c_b)
13888c2ecf20Sopenharmony_ci			data &= ~CLK_GATING_RXMAC_EN;
13898c2ecf20Sopenharmony_ci	} else
13908c2ecf20Sopenharmony_ci		data = 0;
13918c2ecf20Sopenharmony_ci	AT_WRITE_REG(hw, REG_CLK_GATING_CTRL, data);
13928c2ecf20Sopenharmony_ci
13938c2ecf20Sopenharmony_ci	AT_WRITE_REG(hw, REG_INT_RETRIG_TIMER,
13948c2ecf20Sopenharmony_ci		hw->ict & INT_RETRIG_TIMER_MASK);
13958c2ecf20Sopenharmony_ci
13968c2ecf20Sopenharmony_ci	atl1c_configure_des_ring(adapter);
13978c2ecf20Sopenharmony_ci
13988c2ecf20Sopenharmony_ci	if (hw->ctrl_flags & ATL1C_INTR_MODRT_ENABLE) {
13998c2ecf20Sopenharmony_ci		intr_modrt_data = (hw->tx_imt & IRQ_MODRT_TIMER_MASK) <<
14008c2ecf20Sopenharmony_ci					IRQ_MODRT_TX_TIMER_SHIFT;
14018c2ecf20Sopenharmony_ci		intr_modrt_data |= (hw->rx_imt & IRQ_MODRT_TIMER_MASK) <<
14028c2ecf20Sopenharmony_ci					IRQ_MODRT_RX_TIMER_SHIFT;
14038c2ecf20Sopenharmony_ci		AT_WRITE_REG(hw, REG_IRQ_MODRT_TIMER_INIT, intr_modrt_data);
14048c2ecf20Sopenharmony_ci		master_ctrl_data |=
14058c2ecf20Sopenharmony_ci			MASTER_CTRL_TX_ITIMER_EN | MASTER_CTRL_RX_ITIMER_EN;
14068c2ecf20Sopenharmony_ci	}
14078c2ecf20Sopenharmony_ci
14088c2ecf20Sopenharmony_ci	if (hw->ctrl_flags & ATL1C_INTR_CLEAR_ON_READ)
14098c2ecf20Sopenharmony_ci		master_ctrl_data |= MASTER_CTRL_INT_RDCLR;
14108c2ecf20Sopenharmony_ci
14118c2ecf20Sopenharmony_ci	master_ctrl_data |= MASTER_CTRL_SA_TIMER_EN;
14128c2ecf20Sopenharmony_ci	AT_WRITE_REG(hw, REG_MASTER_CTRL, master_ctrl_data);
14138c2ecf20Sopenharmony_ci
14148c2ecf20Sopenharmony_ci	AT_WRITE_REG(hw, REG_SMB_STAT_TIMER,
14158c2ecf20Sopenharmony_ci		hw->smb_timer & SMB_STAT_TIMER_MASK);
14168c2ecf20Sopenharmony_ci
14178c2ecf20Sopenharmony_ci	/* set MTU */
14188c2ecf20Sopenharmony_ci	AT_WRITE_REG(hw, REG_MTU, hw->max_frame_size + ETH_HLEN +
14198c2ecf20Sopenharmony_ci			VLAN_HLEN + ETH_FCS_LEN);
14208c2ecf20Sopenharmony_ci
14218c2ecf20Sopenharmony_ci	atl1c_configure_tx(adapter);
14228c2ecf20Sopenharmony_ci	atl1c_configure_rx(adapter);
14238c2ecf20Sopenharmony_ci	atl1c_configure_dma(adapter);
14248c2ecf20Sopenharmony_ci
14258c2ecf20Sopenharmony_ci	return 0;
14268c2ecf20Sopenharmony_ci}
14278c2ecf20Sopenharmony_ci
14288c2ecf20Sopenharmony_cistatic int atl1c_configure(struct atl1c_adapter *adapter)
14298c2ecf20Sopenharmony_ci{
14308c2ecf20Sopenharmony_ci	struct net_device *netdev = adapter->netdev;
14318c2ecf20Sopenharmony_ci	int num;
14328c2ecf20Sopenharmony_ci
14338c2ecf20Sopenharmony_ci	atl1c_init_ring_ptrs(adapter);
14348c2ecf20Sopenharmony_ci	atl1c_set_multi(netdev);
14358c2ecf20Sopenharmony_ci	atl1c_restore_vlan(adapter);
14368c2ecf20Sopenharmony_ci
14378c2ecf20Sopenharmony_ci	num = atl1c_alloc_rx_buffer(adapter);
14388c2ecf20Sopenharmony_ci	if (unlikely(num == 0))
14398c2ecf20Sopenharmony_ci		return -ENOMEM;
14408c2ecf20Sopenharmony_ci
14418c2ecf20Sopenharmony_ci	if (atl1c_configure_mac(adapter))
14428c2ecf20Sopenharmony_ci		return -EIO;
14438c2ecf20Sopenharmony_ci
14448c2ecf20Sopenharmony_ci	return 0;
14458c2ecf20Sopenharmony_ci}
14468c2ecf20Sopenharmony_ci
14478c2ecf20Sopenharmony_cistatic void atl1c_update_hw_stats(struct atl1c_adapter *adapter)
14488c2ecf20Sopenharmony_ci{
14498c2ecf20Sopenharmony_ci	u16 hw_reg_addr = 0;
14508c2ecf20Sopenharmony_ci	unsigned long *stats_item = NULL;
14518c2ecf20Sopenharmony_ci	u32 data;
14528c2ecf20Sopenharmony_ci
14538c2ecf20Sopenharmony_ci	/* update rx status */
14548c2ecf20Sopenharmony_ci	hw_reg_addr = REG_MAC_RX_STATUS_BIN;
14558c2ecf20Sopenharmony_ci	stats_item  = &adapter->hw_stats.rx_ok;
14568c2ecf20Sopenharmony_ci	while (hw_reg_addr <= REG_MAC_RX_STATUS_END) {
14578c2ecf20Sopenharmony_ci		AT_READ_REG(&adapter->hw, hw_reg_addr, &data);
14588c2ecf20Sopenharmony_ci		*stats_item += data;
14598c2ecf20Sopenharmony_ci		stats_item++;
14608c2ecf20Sopenharmony_ci		hw_reg_addr += 4;
14618c2ecf20Sopenharmony_ci	}
14628c2ecf20Sopenharmony_ci/* update tx status */
14638c2ecf20Sopenharmony_ci	hw_reg_addr = REG_MAC_TX_STATUS_BIN;
14648c2ecf20Sopenharmony_ci	stats_item  = &adapter->hw_stats.tx_ok;
14658c2ecf20Sopenharmony_ci	while (hw_reg_addr <= REG_MAC_TX_STATUS_END) {
14668c2ecf20Sopenharmony_ci		AT_READ_REG(&adapter->hw, hw_reg_addr, &data);
14678c2ecf20Sopenharmony_ci		*stats_item += data;
14688c2ecf20Sopenharmony_ci		stats_item++;
14698c2ecf20Sopenharmony_ci		hw_reg_addr += 4;
14708c2ecf20Sopenharmony_ci	}
14718c2ecf20Sopenharmony_ci}
14728c2ecf20Sopenharmony_ci
14738c2ecf20Sopenharmony_ci/**
14748c2ecf20Sopenharmony_ci * atl1c_get_stats - Get System Network Statistics
14758c2ecf20Sopenharmony_ci * @netdev: network interface device structure
14768c2ecf20Sopenharmony_ci *
14778c2ecf20Sopenharmony_ci * Returns the address of the device statistics structure.
14788c2ecf20Sopenharmony_ci * The statistics are actually updated from the timer callback.
14798c2ecf20Sopenharmony_ci */
14808c2ecf20Sopenharmony_cistatic struct net_device_stats *atl1c_get_stats(struct net_device *netdev)
14818c2ecf20Sopenharmony_ci{
14828c2ecf20Sopenharmony_ci	struct atl1c_adapter *adapter = netdev_priv(netdev);
14838c2ecf20Sopenharmony_ci	struct atl1c_hw_stats  *hw_stats = &adapter->hw_stats;
14848c2ecf20Sopenharmony_ci	struct net_device_stats *net_stats = &netdev->stats;
14858c2ecf20Sopenharmony_ci
14868c2ecf20Sopenharmony_ci	atl1c_update_hw_stats(adapter);
14878c2ecf20Sopenharmony_ci	net_stats->rx_bytes   = hw_stats->rx_byte_cnt;
14888c2ecf20Sopenharmony_ci	net_stats->tx_bytes   = hw_stats->tx_byte_cnt;
14898c2ecf20Sopenharmony_ci	net_stats->multicast  = hw_stats->rx_mcast;
14908c2ecf20Sopenharmony_ci	net_stats->collisions = hw_stats->tx_1_col +
14918c2ecf20Sopenharmony_ci				hw_stats->tx_2_col +
14928c2ecf20Sopenharmony_ci				hw_stats->tx_late_col +
14938c2ecf20Sopenharmony_ci				hw_stats->tx_abort_col;
14948c2ecf20Sopenharmony_ci
14958c2ecf20Sopenharmony_ci	net_stats->rx_errors  = hw_stats->rx_frag +
14968c2ecf20Sopenharmony_ci				hw_stats->rx_fcs_err +
14978c2ecf20Sopenharmony_ci				hw_stats->rx_len_err +
14988c2ecf20Sopenharmony_ci				hw_stats->rx_sz_ov +
14998c2ecf20Sopenharmony_ci				hw_stats->rx_rrd_ov +
15008c2ecf20Sopenharmony_ci				hw_stats->rx_align_err +
15018c2ecf20Sopenharmony_ci				hw_stats->rx_rxf_ov;
15028c2ecf20Sopenharmony_ci
15038c2ecf20Sopenharmony_ci	net_stats->rx_fifo_errors   = hw_stats->rx_rxf_ov;
15048c2ecf20Sopenharmony_ci	net_stats->rx_length_errors = hw_stats->rx_len_err;
15058c2ecf20Sopenharmony_ci	net_stats->rx_crc_errors    = hw_stats->rx_fcs_err;
15068c2ecf20Sopenharmony_ci	net_stats->rx_frame_errors  = hw_stats->rx_align_err;
15078c2ecf20Sopenharmony_ci	net_stats->rx_dropped       = hw_stats->rx_rrd_ov;
15088c2ecf20Sopenharmony_ci
15098c2ecf20Sopenharmony_ci	net_stats->tx_errors = hw_stats->tx_late_col +
15108c2ecf20Sopenharmony_ci			       hw_stats->tx_abort_col +
15118c2ecf20Sopenharmony_ci			       hw_stats->tx_underrun +
15128c2ecf20Sopenharmony_ci			       hw_stats->tx_trunc;
15138c2ecf20Sopenharmony_ci
15148c2ecf20Sopenharmony_ci	net_stats->tx_fifo_errors    = hw_stats->tx_underrun;
15158c2ecf20Sopenharmony_ci	net_stats->tx_aborted_errors = hw_stats->tx_abort_col;
15168c2ecf20Sopenharmony_ci	net_stats->tx_window_errors  = hw_stats->tx_late_col;
15178c2ecf20Sopenharmony_ci
15188c2ecf20Sopenharmony_ci	net_stats->rx_packets = hw_stats->rx_ok + net_stats->rx_errors;
15198c2ecf20Sopenharmony_ci	net_stats->tx_packets = hw_stats->tx_ok + net_stats->tx_errors;
15208c2ecf20Sopenharmony_ci
15218c2ecf20Sopenharmony_ci	return net_stats;
15228c2ecf20Sopenharmony_ci}
15238c2ecf20Sopenharmony_ci
15248c2ecf20Sopenharmony_cistatic inline void atl1c_clear_phy_int(struct atl1c_adapter *adapter)
15258c2ecf20Sopenharmony_ci{
15268c2ecf20Sopenharmony_ci	u16 phy_data;
15278c2ecf20Sopenharmony_ci
15288c2ecf20Sopenharmony_ci	spin_lock(&adapter->mdio_lock);
15298c2ecf20Sopenharmony_ci	atl1c_read_phy_reg(&adapter->hw, MII_ISR, &phy_data);
15308c2ecf20Sopenharmony_ci	spin_unlock(&adapter->mdio_lock);
15318c2ecf20Sopenharmony_ci}
15328c2ecf20Sopenharmony_ci
15338c2ecf20Sopenharmony_cistatic bool atl1c_clean_tx_irq(struct atl1c_adapter *adapter,
15348c2ecf20Sopenharmony_ci				enum atl1c_trans_queue type)
15358c2ecf20Sopenharmony_ci{
15368c2ecf20Sopenharmony_ci	struct atl1c_tpd_ring *tpd_ring = &adapter->tpd_ring[type];
15378c2ecf20Sopenharmony_ci	struct atl1c_buffer *buffer_info;
15388c2ecf20Sopenharmony_ci	struct pci_dev *pdev = adapter->pdev;
15398c2ecf20Sopenharmony_ci	u16 next_to_clean = atomic_read(&tpd_ring->next_to_clean);
15408c2ecf20Sopenharmony_ci	u16 hw_next_to_clean;
15418c2ecf20Sopenharmony_ci	u16 reg;
15428c2ecf20Sopenharmony_ci	unsigned int total_bytes = 0, total_packets = 0;
15438c2ecf20Sopenharmony_ci
15448c2ecf20Sopenharmony_ci	reg = type == atl1c_trans_high ? REG_TPD_PRI1_CIDX : REG_TPD_PRI0_CIDX;
15458c2ecf20Sopenharmony_ci
15468c2ecf20Sopenharmony_ci	AT_READ_REGW(&adapter->hw, reg, &hw_next_to_clean);
15478c2ecf20Sopenharmony_ci
15488c2ecf20Sopenharmony_ci	while (next_to_clean != hw_next_to_clean) {
15498c2ecf20Sopenharmony_ci		buffer_info = &tpd_ring->buffer_info[next_to_clean];
15508c2ecf20Sopenharmony_ci		if (buffer_info->skb) {
15518c2ecf20Sopenharmony_ci			total_bytes += buffer_info->skb->len;
15528c2ecf20Sopenharmony_ci			total_packets++;
15538c2ecf20Sopenharmony_ci		}
15548c2ecf20Sopenharmony_ci		atl1c_clean_buffer(pdev, buffer_info);
15558c2ecf20Sopenharmony_ci		if (++next_to_clean == tpd_ring->count)
15568c2ecf20Sopenharmony_ci			next_to_clean = 0;
15578c2ecf20Sopenharmony_ci		atomic_set(&tpd_ring->next_to_clean, next_to_clean);
15588c2ecf20Sopenharmony_ci	}
15598c2ecf20Sopenharmony_ci
15608c2ecf20Sopenharmony_ci	netdev_completed_queue(adapter->netdev, total_packets, total_bytes);
15618c2ecf20Sopenharmony_ci
15628c2ecf20Sopenharmony_ci	if (netif_queue_stopped(adapter->netdev) &&
15638c2ecf20Sopenharmony_ci			netif_carrier_ok(adapter->netdev)) {
15648c2ecf20Sopenharmony_ci		netif_wake_queue(adapter->netdev);
15658c2ecf20Sopenharmony_ci	}
15668c2ecf20Sopenharmony_ci
15678c2ecf20Sopenharmony_ci	return true;
15688c2ecf20Sopenharmony_ci}
15698c2ecf20Sopenharmony_ci
15708c2ecf20Sopenharmony_ci/**
15718c2ecf20Sopenharmony_ci * atl1c_intr - Interrupt Handler
15728c2ecf20Sopenharmony_ci * @irq: interrupt number
15738c2ecf20Sopenharmony_ci * @data: pointer to a network interface device structure
15748c2ecf20Sopenharmony_ci */
15758c2ecf20Sopenharmony_cistatic irqreturn_t atl1c_intr(int irq, void *data)
15768c2ecf20Sopenharmony_ci{
15778c2ecf20Sopenharmony_ci	struct net_device *netdev  = data;
15788c2ecf20Sopenharmony_ci	struct atl1c_adapter *adapter = netdev_priv(netdev);
15798c2ecf20Sopenharmony_ci	struct pci_dev *pdev = adapter->pdev;
15808c2ecf20Sopenharmony_ci	struct atl1c_hw *hw = &adapter->hw;
15818c2ecf20Sopenharmony_ci	int max_ints = AT_MAX_INT_WORK;
15828c2ecf20Sopenharmony_ci	int handled = IRQ_NONE;
15838c2ecf20Sopenharmony_ci	u32 status;
15848c2ecf20Sopenharmony_ci	u32 reg_data;
15858c2ecf20Sopenharmony_ci
15868c2ecf20Sopenharmony_ci	do {
15878c2ecf20Sopenharmony_ci		AT_READ_REG(hw, REG_ISR, &reg_data);
15888c2ecf20Sopenharmony_ci		status = reg_data & hw->intr_mask;
15898c2ecf20Sopenharmony_ci
15908c2ecf20Sopenharmony_ci		if (status == 0 || (status & ISR_DIS_INT) != 0) {
15918c2ecf20Sopenharmony_ci			if (max_ints != AT_MAX_INT_WORK)
15928c2ecf20Sopenharmony_ci				handled = IRQ_HANDLED;
15938c2ecf20Sopenharmony_ci			break;
15948c2ecf20Sopenharmony_ci		}
15958c2ecf20Sopenharmony_ci		/* link event */
15968c2ecf20Sopenharmony_ci		if (status & ISR_GPHY)
15978c2ecf20Sopenharmony_ci			atl1c_clear_phy_int(adapter);
15988c2ecf20Sopenharmony_ci		/* Ack ISR */
15998c2ecf20Sopenharmony_ci		AT_WRITE_REG(hw, REG_ISR, status | ISR_DIS_INT);
16008c2ecf20Sopenharmony_ci		if (status & ISR_RX_PKT) {
16018c2ecf20Sopenharmony_ci			if (likely(napi_schedule_prep(&adapter->napi))) {
16028c2ecf20Sopenharmony_ci				hw->intr_mask &= ~ISR_RX_PKT;
16038c2ecf20Sopenharmony_ci				AT_WRITE_REG(hw, REG_IMR, hw->intr_mask);
16048c2ecf20Sopenharmony_ci				__napi_schedule(&adapter->napi);
16058c2ecf20Sopenharmony_ci			}
16068c2ecf20Sopenharmony_ci		}
16078c2ecf20Sopenharmony_ci		if (status & ISR_TX_PKT)
16088c2ecf20Sopenharmony_ci			atl1c_clean_tx_irq(adapter, atl1c_trans_normal);
16098c2ecf20Sopenharmony_ci
16108c2ecf20Sopenharmony_ci		handled = IRQ_HANDLED;
16118c2ecf20Sopenharmony_ci		/* check if PCIE PHY Link down */
16128c2ecf20Sopenharmony_ci		if (status & ISR_ERROR) {
16138c2ecf20Sopenharmony_ci			if (netif_msg_hw(adapter))
16148c2ecf20Sopenharmony_ci				dev_err(&pdev->dev,
16158c2ecf20Sopenharmony_ci					"atl1c hardware error (status = 0x%x)\n",
16168c2ecf20Sopenharmony_ci					status & ISR_ERROR);
16178c2ecf20Sopenharmony_ci			/* reset MAC */
16188c2ecf20Sopenharmony_ci			set_bit(ATL1C_WORK_EVENT_RESET, &adapter->work_event);
16198c2ecf20Sopenharmony_ci			schedule_work(&adapter->common_task);
16208c2ecf20Sopenharmony_ci			return IRQ_HANDLED;
16218c2ecf20Sopenharmony_ci		}
16228c2ecf20Sopenharmony_ci
16238c2ecf20Sopenharmony_ci		if (status & ISR_OVER)
16248c2ecf20Sopenharmony_ci			if (netif_msg_intr(adapter))
16258c2ecf20Sopenharmony_ci				dev_warn(&pdev->dev,
16268c2ecf20Sopenharmony_ci					"TX/RX overflow (status = 0x%x)\n",
16278c2ecf20Sopenharmony_ci					status & ISR_OVER);
16288c2ecf20Sopenharmony_ci
16298c2ecf20Sopenharmony_ci		/* link event */
16308c2ecf20Sopenharmony_ci		if (status & (ISR_GPHY | ISR_MANUAL)) {
16318c2ecf20Sopenharmony_ci			netdev->stats.tx_carrier_errors++;
16328c2ecf20Sopenharmony_ci			atl1c_link_chg_event(adapter);
16338c2ecf20Sopenharmony_ci			break;
16348c2ecf20Sopenharmony_ci		}
16358c2ecf20Sopenharmony_ci
16368c2ecf20Sopenharmony_ci	} while (--max_ints > 0);
16378c2ecf20Sopenharmony_ci	/* re-enable Interrupt*/
16388c2ecf20Sopenharmony_ci	AT_WRITE_REG(&adapter->hw, REG_ISR, 0);
16398c2ecf20Sopenharmony_ci	return handled;
16408c2ecf20Sopenharmony_ci}
16418c2ecf20Sopenharmony_ci
16428c2ecf20Sopenharmony_cistatic inline void atl1c_rx_checksum(struct atl1c_adapter *adapter,
16438c2ecf20Sopenharmony_ci		  struct sk_buff *skb, struct atl1c_recv_ret_status *prrs)
16448c2ecf20Sopenharmony_ci{
16458c2ecf20Sopenharmony_ci	/*
16468c2ecf20Sopenharmony_ci	 * The pid field in RRS in not correct sometimes, so we
16478c2ecf20Sopenharmony_ci	 * cannot figure out if the packet is fragmented or not,
16488c2ecf20Sopenharmony_ci	 * so we tell the KERNEL CHECKSUM_NONE
16498c2ecf20Sopenharmony_ci	 */
16508c2ecf20Sopenharmony_ci	skb_checksum_none_assert(skb);
16518c2ecf20Sopenharmony_ci}
16528c2ecf20Sopenharmony_ci
16538c2ecf20Sopenharmony_cistatic struct sk_buff *atl1c_alloc_skb(struct atl1c_adapter *adapter)
16548c2ecf20Sopenharmony_ci{
16558c2ecf20Sopenharmony_ci	struct sk_buff *skb;
16568c2ecf20Sopenharmony_ci	struct page *page;
16578c2ecf20Sopenharmony_ci
16588c2ecf20Sopenharmony_ci	if (adapter->rx_frag_size > PAGE_SIZE)
16598c2ecf20Sopenharmony_ci		return netdev_alloc_skb(adapter->netdev,
16608c2ecf20Sopenharmony_ci					adapter->rx_buffer_len);
16618c2ecf20Sopenharmony_ci
16628c2ecf20Sopenharmony_ci	page = adapter->rx_page;
16638c2ecf20Sopenharmony_ci	if (!page) {
16648c2ecf20Sopenharmony_ci		adapter->rx_page = page = alloc_page(GFP_ATOMIC);
16658c2ecf20Sopenharmony_ci		if (unlikely(!page))
16668c2ecf20Sopenharmony_ci			return NULL;
16678c2ecf20Sopenharmony_ci		adapter->rx_page_offset = 0;
16688c2ecf20Sopenharmony_ci	}
16698c2ecf20Sopenharmony_ci
16708c2ecf20Sopenharmony_ci	skb = build_skb(page_address(page) + adapter->rx_page_offset,
16718c2ecf20Sopenharmony_ci			adapter->rx_frag_size);
16728c2ecf20Sopenharmony_ci	if (likely(skb)) {
16738c2ecf20Sopenharmony_ci		skb_reserve(skb, NET_SKB_PAD);
16748c2ecf20Sopenharmony_ci		adapter->rx_page_offset += adapter->rx_frag_size;
16758c2ecf20Sopenharmony_ci		if (adapter->rx_page_offset >= PAGE_SIZE)
16768c2ecf20Sopenharmony_ci			adapter->rx_page = NULL;
16778c2ecf20Sopenharmony_ci		else
16788c2ecf20Sopenharmony_ci			get_page(page);
16798c2ecf20Sopenharmony_ci	}
16808c2ecf20Sopenharmony_ci	return skb;
16818c2ecf20Sopenharmony_ci}
16828c2ecf20Sopenharmony_ci
16838c2ecf20Sopenharmony_cistatic int atl1c_alloc_rx_buffer(struct atl1c_adapter *adapter)
16848c2ecf20Sopenharmony_ci{
16858c2ecf20Sopenharmony_ci	struct atl1c_rfd_ring *rfd_ring = &adapter->rfd_ring;
16868c2ecf20Sopenharmony_ci	struct pci_dev *pdev = adapter->pdev;
16878c2ecf20Sopenharmony_ci	struct atl1c_buffer *buffer_info, *next_info;
16888c2ecf20Sopenharmony_ci	struct sk_buff *skb;
16898c2ecf20Sopenharmony_ci	void *vir_addr = NULL;
16908c2ecf20Sopenharmony_ci	u16 num_alloc = 0;
16918c2ecf20Sopenharmony_ci	u16 rfd_next_to_use, next_next;
16928c2ecf20Sopenharmony_ci	struct atl1c_rx_free_desc *rfd_desc;
16938c2ecf20Sopenharmony_ci	dma_addr_t mapping;
16948c2ecf20Sopenharmony_ci
16958c2ecf20Sopenharmony_ci	next_next = rfd_next_to_use = rfd_ring->next_to_use;
16968c2ecf20Sopenharmony_ci	if (++next_next == rfd_ring->count)
16978c2ecf20Sopenharmony_ci		next_next = 0;
16988c2ecf20Sopenharmony_ci	buffer_info = &rfd_ring->buffer_info[rfd_next_to_use];
16998c2ecf20Sopenharmony_ci	next_info = &rfd_ring->buffer_info[next_next];
17008c2ecf20Sopenharmony_ci
17018c2ecf20Sopenharmony_ci	while (next_info->flags & ATL1C_BUFFER_FREE) {
17028c2ecf20Sopenharmony_ci		rfd_desc = ATL1C_RFD_DESC(rfd_ring, rfd_next_to_use);
17038c2ecf20Sopenharmony_ci
17048c2ecf20Sopenharmony_ci		skb = atl1c_alloc_skb(adapter);
17058c2ecf20Sopenharmony_ci		if (unlikely(!skb)) {
17068c2ecf20Sopenharmony_ci			if (netif_msg_rx_err(adapter))
17078c2ecf20Sopenharmony_ci				dev_warn(&pdev->dev, "alloc rx buffer failed\n");
17088c2ecf20Sopenharmony_ci			break;
17098c2ecf20Sopenharmony_ci		}
17108c2ecf20Sopenharmony_ci
17118c2ecf20Sopenharmony_ci		/*
17128c2ecf20Sopenharmony_ci		 * Make buffer alignment 2 beyond a 16 byte boundary
17138c2ecf20Sopenharmony_ci		 * this will result in a 16 byte aligned IP header after
17148c2ecf20Sopenharmony_ci		 * the 14 byte MAC header is removed
17158c2ecf20Sopenharmony_ci		 */
17168c2ecf20Sopenharmony_ci		vir_addr = skb->data;
17178c2ecf20Sopenharmony_ci		ATL1C_SET_BUFFER_STATE(buffer_info, ATL1C_BUFFER_BUSY);
17188c2ecf20Sopenharmony_ci		buffer_info->skb = skb;
17198c2ecf20Sopenharmony_ci		buffer_info->length = adapter->rx_buffer_len;
17208c2ecf20Sopenharmony_ci		mapping = dma_map_single(&pdev->dev, vir_addr,
17218c2ecf20Sopenharmony_ci					 buffer_info->length, DMA_FROM_DEVICE);
17228c2ecf20Sopenharmony_ci		if (unlikely(dma_mapping_error(&pdev->dev, mapping))) {
17238c2ecf20Sopenharmony_ci			dev_kfree_skb(skb);
17248c2ecf20Sopenharmony_ci			buffer_info->skb = NULL;
17258c2ecf20Sopenharmony_ci			buffer_info->length = 0;
17268c2ecf20Sopenharmony_ci			ATL1C_SET_BUFFER_STATE(buffer_info, ATL1C_BUFFER_FREE);
17278c2ecf20Sopenharmony_ci			netif_warn(adapter, rx_err, adapter->netdev, "RX pci_map_single failed");
17288c2ecf20Sopenharmony_ci			break;
17298c2ecf20Sopenharmony_ci		}
17308c2ecf20Sopenharmony_ci		buffer_info->dma = mapping;
17318c2ecf20Sopenharmony_ci		ATL1C_SET_PCIMAP_TYPE(buffer_info, ATL1C_PCIMAP_SINGLE,
17328c2ecf20Sopenharmony_ci			ATL1C_PCIMAP_FROMDEVICE);
17338c2ecf20Sopenharmony_ci		rfd_desc->buffer_addr = cpu_to_le64(buffer_info->dma);
17348c2ecf20Sopenharmony_ci		rfd_next_to_use = next_next;
17358c2ecf20Sopenharmony_ci		if (++next_next == rfd_ring->count)
17368c2ecf20Sopenharmony_ci			next_next = 0;
17378c2ecf20Sopenharmony_ci		buffer_info = &rfd_ring->buffer_info[rfd_next_to_use];
17388c2ecf20Sopenharmony_ci		next_info = &rfd_ring->buffer_info[next_next];
17398c2ecf20Sopenharmony_ci		num_alloc++;
17408c2ecf20Sopenharmony_ci	}
17418c2ecf20Sopenharmony_ci
17428c2ecf20Sopenharmony_ci	if (num_alloc) {
17438c2ecf20Sopenharmony_ci		/* TODO: update mailbox here */
17448c2ecf20Sopenharmony_ci		wmb();
17458c2ecf20Sopenharmony_ci		rfd_ring->next_to_use = rfd_next_to_use;
17468c2ecf20Sopenharmony_ci		AT_WRITE_REG(&adapter->hw, REG_MB_RFD0_PROD_IDX,
17478c2ecf20Sopenharmony_ci			rfd_ring->next_to_use & MB_RFDX_PROD_IDX_MASK);
17488c2ecf20Sopenharmony_ci	}
17498c2ecf20Sopenharmony_ci
17508c2ecf20Sopenharmony_ci	return num_alloc;
17518c2ecf20Sopenharmony_ci}
17528c2ecf20Sopenharmony_ci
17538c2ecf20Sopenharmony_cistatic void atl1c_clean_rrd(struct atl1c_rrd_ring *rrd_ring,
17548c2ecf20Sopenharmony_ci			struct	atl1c_recv_ret_status *rrs, u16 num)
17558c2ecf20Sopenharmony_ci{
17568c2ecf20Sopenharmony_ci	u16 i;
17578c2ecf20Sopenharmony_ci	/* the relationship between rrd and rfd is one map one */
17588c2ecf20Sopenharmony_ci	for (i = 0; i < num; i++, rrs = ATL1C_RRD_DESC(rrd_ring,
17598c2ecf20Sopenharmony_ci					rrd_ring->next_to_clean)) {
17608c2ecf20Sopenharmony_ci		rrs->word3 &= ~RRS_RXD_UPDATED;
17618c2ecf20Sopenharmony_ci		if (++rrd_ring->next_to_clean == rrd_ring->count)
17628c2ecf20Sopenharmony_ci			rrd_ring->next_to_clean = 0;
17638c2ecf20Sopenharmony_ci	}
17648c2ecf20Sopenharmony_ci}
17658c2ecf20Sopenharmony_ci
17668c2ecf20Sopenharmony_cistatic void atl1c_clean_rfd(struct atl1c_rfd_ring *rfd_ring,
17678c2ecf20Sopenharmony_ci	struct atl1c_recv_ret_status *rrs, u16 num)
17688c2ecf20Sopenharmony_ci{
17698c2ecf20Sopenharmony_ci	u16 i;
17708c2ecf20Sopenharmony_ci	u16 rfd_index;
17718c2ecf20Sopenharmony_ci	struct atl1c_buffer *buffer_info = rfd_ring->buffer_info;
17728c2ecf20Sopenharmony_ci
17738c2ecf20Sopenharmony_ci	rfd_index = (rrs->word0 >> RRS_RX_RFD_INDEX_SHIFT) &
17748c2ecf20Sopenharmony_ci			RRS_RX_RFD_INDEX_MASK;
17758c2ecf20Sopenharmony_ci	for (i = 0; i < num; i++) {
17768c2ecf20Sopenharmony_ci		buffer_info[rfd_index].skb = NULL;
17778c2ecf20Sopenharmony_ci		ATL1C_SET_BUFFER_STATE(&buffer_info[rfd_index],
17788c2ecf20Sopenharmony_ci					ATL1C_BUFFER_FREE);
17798c2ecf20Sopenharmony_ci		if (++rfd_index == rfd_ring->count)
17808c2ecf20Sopenharmony_ci			rfd_index = 0;
17818c2ecf20Sopenharmony_ci	}
17828c2ecf20Sopenharmony_ci	rfd_ring->next_to_clean = rfd_index;
17838c2ecf20Sopenharmony_ci}
17848c2ecf20Sopenharmony_ci
17858c2ecf20Sopenharmony_cistatic void atl1c_clean_rx_irq(struct atl1c_adapter *adapter,
17868c2ecf20Sopenharmony_ci		   int *work_done, int work_to_do)
17878c2ecf20Sopenharmony_ci{
17888c2ecf20Sopenharmony_ci	u16 rfd_num, rfd_index;
17898c2ecf20Sopenharmony_ci	u16 count = 0;
17908c2ecf20Sopenharmony_ci	u16 length;
17918c2ecf20Sopenharmony_ci	struct pci_dev *pdev = adapter->pdev;
17928c2ecf20Sopenharmony_ci	struct net_device *netdev  = adapter->netdev;
17938c2ecf20Sopenharmony_ci	struct atl1c_rfd_ring *rfd_ring = &adapter->rfd_ring;
17948c2ecf20Sopenharmony_ci	struct atl1c_rrd_ring *rrd_ring = &adapter->rrd_ring;
17958c2ecf20Sopenharmony_ci	struct sk_buff *skb;
17968c2ecf20Sopenharmony_ci	struct atl1c_recv_ret_status *rrs;
17978c2ecf20Sopenharmony_ci	struct atl1c_buffer *buffer_info;
17988c2ecf20Sopenharmony_ci
17998c2ecf20Sopenharmony_ci	while (1) {
18008c2ecf20Sopenharmony_ci		if (*work_done >= work_to_do)
18018c2ecf20Sopenharmony_ci			break;
18028c2ecf20Sopenharmony_ci		rrs = ATL1C_RRD_DESC(rrd_ring, rrd_ring->next_to_clean);
18038c2ecf20Sopenharmony_ci		if (likely(RRS_RXD_IS_VALID(rrs->word3))) {
18048c2ecf20Sopenharmony_ci			rfd_num = (rrs->word0 >> RRS_RX_RFD_CNT_SHIFT) &
18058c2ecf20Sopenharmony_ci				RRS_RX_RFD_CNT_MASK;
18068c2ecf20Sopenharmony_ci			if (unlikely(rfd_num != 1))
18078c2ecf20Sopenharmony_ci				/* TODO support mul rfd*/
18088c2ecf20Sopenharmony_ci				if (netif_msg_rx_err(adapter))
18098c2ecf20Sopenharmony_ci					dev_warn(&pdev->dev,
18108c2ecf20Sopenharmony_ci						"Multi rfd not support yet!\n");
18118c2ecf20Sopenharmony_ci			goto rrs_checked;
18128c2ecf20Sopenharmony_ci		} else {
18138c2ecf20Sopenharmony_ci			break;
18148c2ecf20Sopenharmony_ci		}
18158c2ecf20Sopenharmony_cirrs_checked:
18168c2ecf20Sopenharmony_ci		atl1c_clean_rrd(rrd_ring, rrs, rfd_num);
18178c2ecf20Sopenharmony_ci		if (rrs->word3 & (RRS_RX_ERR_SUM | RRS_802_3_LEN_ERR)) {
18188c2ecf20Sopenharmony_ci			atl1c_clean_rfd(rfd_ring, rrs, rfd_num);
18198c2ecf20Sopenharmony_ci			if (netif_msg_rx_err(adapter))
18208c2ecf20Sopenharmony_ci				dev_warn(&pdev->dev,
18218c2ecf20Sopenharmony_ci					 "wrong packet! rrs word3 is %x\n",
18228c2ecf20Sopenharmony_ci					 rrs->word3);
18238c2ecf20Sopenharmony_ci			continue;
18248c2ecf20Sopenharmony_ci		}
18258c2ecf20Sopenharmony_ci
18268c2ecf20Sopenharmony_ci		length = le16_to_cpu((rrs->word3 >> RRS_PKT_SIZE_SHIFT) &
18278c2ecf20Sopenharmony_ci				RRS_PKT_SIZE_MASK);
18288c2ecf20Sopenharmony_ci		/* Good Receive */
18298c2ecf20Sopenharmony_ci		if (likely(rfd_num == 1)) {
18308c2ecf20Sopenharmony_ci			rfd_index = (rrs->word0 >> RRS_RX_RFD_INDEX_SHIFT) &
18318c2ecf20Sopenharmony_ci					RRS_RX_RFD_INDEX_MASK;
18328c2ecf20Sopenharmony_ci			buffer_info = &rfd_ring->buffer_info[rfd_index];
18338c2ecf20Sopenharmony_ci			dma_unmap_single(&pdev->dev, buffer_info->dma,
18348c2ecf20Sopenharmony_ci					 buffer_info->length, DMA_FROM_DEVICE);
18358c2ecf20Sopenharmony_ci			skb = buffer_info->skb;
18368c2ecf20Sopenharmony_ci		} else {
18378c2ecf20Sopenharmony_ci			/* TODO */
18388c2ecf20Sopenharmony_ci			if (netif_msg_rx_err(adapter))
18398c2ecf20Sopenharmony_ci				dev_warn(&pdev->dev,
18408c2ecf20Sopenharmony_ci					"Multi rfd not support yet!\n");
18418c2ecf20Sopenharmony_ci			break;
18428c2ecf20Sopenharmony_ci		}
18438c2ecf20Sopenharmony_ci		atl1c_clean_rfd(rfd_ring, rrs, rfd_num);
18448c2ecf20Sopenharmony_ci		skb_put(skb, length - ETH_FCS_LEN);
18458c2ecf20Sopenharmony_ci		skb->protocol = eth_type_trans(skb, netdev);
18468c2ecf20Sopenharmony_ci		atl1c_rx_checksum(adapter, skb, rrs);
18478c2ecf20Sopenharmony_ci		if (rrs->word3 & RRS_VLAN_INS) {
18488c2ecf20Sopenharmony_ci			u16 vlan;
18498c2ecf20Sopenharmony_ci
18508c2ecf20Sopenharmony_ci			AT_TAG_TO_VLAN(rrs->vlan_tag, vlan);
18518c2ecf20Sopenharmony_ci			vlan = le16_to_cpu(vlan);
18528c2ecf20Sopenharmony_ci			__vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vlan);
18538c2ecf20Sopenharmony_ci		}
18548c2ecf20Sopenharmony_ci		netif_receive_skb(skb);
18558c2ecf20Sopenharmony_ci
18568c2ecf20Sopenharmony_ci		(*work_done)++;
18578c2ecf20Sopenharmony_ci		count++;
18588c2ecf20Sopenharmony_ci	}
18598c2ecf20Sopenharmony_ci	if (count)
18608c2ecf20Sopenharmony_ci		atl1c_alloc_rx_buffer(adapter);
18618c2ecf20Sopenharmony_ci}
18628c2ecf20Sopenharmony_ci
18638c2ecf20Sopenharmony_ci/**
18648c2ecf20Sopenharmony_ci * atl1c_clean - NAPI Rx polling callback
18658c2ecf20Sopenharmony_ci * @napi: napi info
18668c2ecf20Sopenharmony_ci * @budget: limit of packets to clean
18678c2ecf20Sopenharmony_ci */
18688c2ecf20Sopenharmony_cistatic int atl1c_clean(struct napi_struct *napi, int budget)
18698c2ecf20Sopenharmony_ci{
18708c2ecf20Sopenharmony_ci	struct atl1c_adapter *adapter =
18718c2ecf20Sopenharmony_ci			container_of(napi, struct atl1c_adapter, napi);
18728c2ecf20Sopenharmony_ci	int work_done = 0;
18738c2ecf20Sopenharmony_ci
18748c2ecf20Sopenharmony_ci	/* Keep link state information with original netdev */
18758c2ecf20Sopenharmony_ci	if (!netif_carrier_ok(adapter->netdev))
18768c2ecf20Sopenharmony_ci		goto quit_polling;
18778c2ecf20Sopenharmony_ci	/* just enable one RXQ */
18788c2ecf20Sopenharmony_ci	atl1c_clean_rx_irq(adapter, &work_done, budget);
18798c2ecf20Sopenharmony_ci
18808c2ecf20Sopenharmony_ci	if (work_done < budget) {
18818c2ecf20Sopenharmony_ciquit_polling:
18828c2ecf20Sopenharmony_ci		napi_complete_done(napi, work_done);
18838c2ecf20Sopenharmony_ci		adapter->hw.intr_mask |= ISR_RX_PKT;
18848c2ecf20Sopenharmony_ci		AT_WRITE_REG(&adapter->hw, REG_IMR, adapter->hw.intr_mask);
18858c2ecf20Sopenharmony_ci	}
18868c2ecf20Sopenharmony_ci	return work_done;
18878c2ecf20Sopenharmony_ci}
18888c2ecf20Sopenharmony_ci
18898c2ecf20Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER
18908c2ecf20Sopenharmony_ci
18918c2ecf20Sopenharmony_ci/*
18928c2ecf20Sopenharmony_ci * Polling 'interrupt' - used by things like netconsole to send skbs
18938c2ecf20Sopenharmony_ci * without having to re-enable interrupts. It's not called while
18948c2ecf20Sopenharmony_ci * the interrupt routine is executing.
18958c2ecf20Sopenharmony_ci */
18968c2ecf20Sopenharmony_cistatic void atl1c_netpoll(struct net_device *netdev)
18978c2ecf20Sopenharmony_ci{
18988c2ecf20Sopenharmony_ci	struct atl1c_adapter *adapter = netdev_priv(netdev);
18998c2ecf20Sopenharmony_ci
19008c2ecf20Sopenharmony_ci	disable_irq(adapter->pdev->irq);
19018c2ecf20Sopenharmony_ci	atl1c_intr(adapter->pdev->irq, netdev);
19028c2ecf20Sopenharmony_ci	enable_irq(adapter->pdev->irq);
19038c2ecf20Sopenharmony_ci}
19048c2ecf20Sopenharmony_ci#endif
19058c2ecf20Sopenharmony_ci
19068c2ecf20Sopenharmony_cistatic inline u16 atl1c_tpd_avail(struct atl1c_adapter *adapter, enum atl1c_trans_queue type)
19078c2ecf20Sopenharmony_ci{
19088c2ecf20Sopenharmony_ci	struct atl1c_tpd_ring *tpd_ring = &adapter->tpd_ring[type];
19098c2ecf20Sopenharmony_ci	u16 next_to_use = 0;
19108c2ecf20Sopenharmony_ci	u16 next_to_clean = 0;
19118c2ecf20Sopenharmony_ci
19128c2ecf20Sopenharmony_ci	next_to_clean = atomic_read(&tpd_ring->next_to_clean);
19138c2ecf20Sopenharmony_ci	next_to_use   = tpd_ring->next_to_use;
19148c2ecf20Sopenharmony_ci
19158c2ecf20Sopenharmony_ci	return (u16)(next_to_clean > next_to_use) ?
19168c2ecf20Sopenharmony_ci		(next_to_clean - next_to_use - 1) :
19178c2ecf20Sopenharmony_ci		(tpd_ring->count + next_to_clean - next_to_use - 1);
19188c2ecf20Sopenharmony_ci}
19198c2ecf20Sopenharmony_ci
19208c2ecf20Sopenharmony_ci/*
19218c2ecf20Sopenharmony_ci * get next usable tpd
19228c2ecf20Sopenharmony_ci * Note: should call atl1c_tdp_avail to make sure
19238c2ecf20Sopenharmony_ci * there is enough tpd to use
19248c2ecf20Sopenharmony_ci */
19258c2ecf20Sopenharmony_cistatic struct atl1c_tpd_desc *atl1c_get_tpd(struct atl1c_adapter *adapter,
19268c2ecf20Sopenharmony_ci	enum atl1c_trans_queue type)
19278c2ecf20Sopenharmony_ci{
19288c2ecf20Sopenharmony_ci	struct atl1c_tpd_ring *tpd_ring = &adapter->tpd_ring[type];
19298c2ecf20Sopenharmony_ci	struct atl1c_tpd_desc *tpd_desc;
19308c2ecf20Sopenharmony_ci	u16 next_to_use = 0;
19318c2ecf20Sopenharmony_ci
19328c2ecf20Sopenharmony_ci	next_to_use = tpd_ring->next_to_use;
19338c2ecf20Sopenharmony_ci	if (++tpd_ring->next_to_use == tpd_ring->count)
19348c2ecf20Sopenharmony_ci		tpd_ring->next_to_use = 0;
19358c2ecf20Sopenharmony_ci	tpd_desc = ATL1C_TPD_DESC(tpd_ring, next_to_use);
19368c2ecf20Sopenharmony_ci	memset(tpd_desc, 0, sizeof(struct atl1c_tpd_desc));
19378c2ecf20Sopenharmony_ci	return	tpd_desc;
19388c2ecf20Sopenharmony_ci}
19398c2ecf20Sopenharmony_ci
19408c2ecf20Sopenharmony_cistatic struct atl1c_buffer *
19418c2ecf20Sopenharmony_ciatl1c_get_tx_buffer(struct atl1c_adapter *adapter, struct atl1c_tpd_desc *tpd)
19428c2ecf20Sopenharmony_ci{
19438c2ecf20Sopenharmony_ci	struct atl1c_tpd_ring *tpd_ring = adapter->tpd_ring;
19448c2ecf20Sopenharmony_ci
19458c2ecf20Sopenharmony_ci	return &tpd_ring->buffer_info[tpd -
19468c2ecf20Sopenharmony_ci			(struct atl1c_tpd_desc *)tpd_ring->desc];
19478c2ecf20Sopenharmony_ci}
19488c2ecf20Sopenharmony_ci
19498c2ecf20Sopenharmony_ci/* Calculate the transmit packet descript needed*/
19508c2ecf20Sopenharmony_cistatic u16 atl1c_cal_tpd_req(const struct sk_buff *skb)
19518c2ecf20Sopenharmony_ci{
19528c2ecf20Sopenharmony_ci	u16 tpd_req;
19538c2ecf20Sopenharmony_ci	u16 proto_hdr_len = 0;
19548c2ecf20Sopenharmony_ci
19558c2ecf20Sopenharmony_ci	tpd_req = skb_shinfo(skb)->nr_frags + 1;
19568c2ecf20Sopenharmony_ci
19578c2ecf20Sopenharmony_ci	if (skb_is_gso(skb)) {
19588c2ecf20Sopenharmony_ci		proto_hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb);
19598c2ecf20Sopenharmony_ci		if (proto_hdr_len < skb_headlen(skb))
19608c2ecf20Sopenharmony_ci			tpd_req++;
19618c2ecf20Sopenharmony_ci		if (skb_shinfo(skb)->gso_type & SKB_GSO_TCPV6)
19628c2ecf20Sopenharmony_ci			tpd_req++;
19638c2ecf20Sopenharmony_ci	}
19648c2ecf20Sopenharmony_ci	return tpd_req;
19658c2ecf20Sopenharmony_ci}
19668c2ecf20Sopenharmony_ci
19678c2ecf20Sopenharmony_cistatic int atl1c_tso_csum(struct atl1c_adapter *adapter,
19688c2ecf20Sopenharmony_ci			  struct sk_buff *skb,
19698c2ecf20Sopenharmony_ci			  struct atl1c_tpd_desc **tpd,
19708c2ecf20Sopenharmony_ci			  enum atl1c_trans_queue type)
19718c2ecf20Sopenharmony_ci{
19728c2ecf20Sopenharmony_ci	struct pci_dev *pdev = adapter->pdev;
19738c2ecf20Sopenharmony_ci	unsigned short offload_type;
19748c2ecf20Sopenharmony_ci	u8 hdr_len;
19758c2ecf20Sopenharmony_ci	u32 real_len;
19768c2ecf20Sopenharmony_ci
19778c2ecf20Sopenharmony_ci	if (skb_is_gso(skb)) {
19788c2ecf20Sopenharmony_ci		int err;
19798c2ecf20Sopenharmony_ci
19808c2ecf20Sopenharmony_ci		err = skb_cow_head(skb, 0);
19818c2ecf20Sopenharmony_ci		if (err < 0)
19828c2ecf20Sopenharmony_ci			return err;
19838c2ecf20Sopenharmony_ci
19848c2ecf20Sopenharmony_ci		offload_type = skb_shinfo(skb)->gso_type;
19858c2ecf20Sopenharmony_ci
19868c2ecf20Sopenharmony_ci		if (offload_type & SKB_GSO_TCPV4) {
19878c2ecf20Sopenharmony_ci			real_len = (((unsigned char *)ip_hdr(skb) - skb->data)
19888c2ecf20Sopenharmony_ci					+ ntohs(ip_hdr(skb)->tot_len));
19898c2ecf20Sopenharmony_ci
19908c2ecf20Sopenharmony_ci			if (real_len < skb->len) {
19918c2ecf20Sopenharmony_ci				err = pskb_trim(skb, real_len);
19928c2ecf20Sopenharmony_ci				if (err)
19938c2ecf20Sopenharmony_ci					return err;
19948c2ecf20Sopenharmony_ci			}
19958c2ecf20Sopenharmony_ci
19968c2ecf20Sopenharmony_ci			hdr_len = (skb_transport_offset(skb) + tcp_hdrlen(skb));
19978c2ecf20Sopenharmony_ci			if (unlikely(skb->len == hdr_len)) {
19988c2ecf20Sopenharmony_ci				/* only xsum need */
19998c2ecf20Sopenharmony_ci				if (netif_msg_tx_queued(adapter))
20008c2ecf20Sopenharmony_ci					dev_warn(&pdev->dev,
20018c2ecf20Sopenharmony_ci						"IPV4 tso with zero data??\n");
20028c2ecf20Sopenharmony_ci				goto check_sum;
20038c2ecf20Sopenharmony_ci			} else {
20048c2ecf20Sopenharmony_ci				ip_hdr(skb)->check = 0;
20058c2ecf20Sopenharmony_ci				tcp_hdr(skb)->check = ~csum_tcpudp_magic(
20068c2ecf20Sopenharmony_ci							ip_hdr(skb)->saddr,
20078c2ecf20Sopenharmony_ci							ip_hdr(skb)->daddr,
20088c2ecf20Sopenharmony_ci							0, IPPROTO_TCP, 0);
20098c2ecf20Sopenharmony_ci				(*tpd)->word1 |= 1 << TPD_IPV4_PACKET_SHIFT;
20108c2ecf20Sopenharmony_ci			}
20118c2ecf20Sopenharmony_ci		}
20128c2ecf20Sopenharmony_ci
20138c2ecf20Sopenharmony_ci		if (offload_type & SKB_GSO_TCPV6) {
20148c2ecf20Sopenharmony_ci			struct atl1c_tpd_ext_desc *etpd =
20158c2ecf20Sopenharmony_ci				*(struct atl1c_tpd_ext_desc **)(tpd);
20168c2ecf20Sopenharmony_ci
20178c2ecf20Sopenharmony_ci			memset(etpd, 0, sizeof(struct atl1c_tpd_ext_desc));
20188c2ecf20Sopenharmony_ci			*tpd = atl1c_get_tpd(adapter, type);
20198c2ecf20Sopenharmony_ci			ipv6_hdr(skb)->payload_len = 0;
20208c2ecf20Sopenharmony_ci			/* check payload == 0 byte ? */
20218c2ecf20Sopenharmony_ci			hdr_len = (skb_transport_offset(skb) + tcp_hdrlen(skb));
20228c2ecf20Sopenharmony_ci			if (unlikely(skb->len == hdr_len)) {
20238c2ecf20Sopenharmony_ci				/* only xsum need */
20248c2ecf20Sopenharmony_ci				if (netif_msg_tx_queued(adapter))
20258c2ecf20Sopenharmony_ci					dev_warn(&pdev->dev,
20268c2ecf20Sopenharmony_ci						"IPV6 tso with zero data??\n");
20278c2ecf20Sopenharmony_ci				goto check_sum;
20288c2ecf20Sopenharmony_ci			} else
20298c2ecf20Sopenharmony_ci				tcp_v6_gso_csum_prep(skb);
20308c2ecf20Sopenharmony_ci
20318c2ecf20Sopenharmony_ci			etpd->word1 |= 1 << TPD_LSO_EN_SHIFT;
20328c2ecf20Sopenharmony_ci			etpd->word1 |= 1 << TPD_LSO_VER_SHIFT;
20338c2ecf20Sopenharmony_ci			etpd->pkt_len = cpu_to_le32(skb->len);
20348c2ecf20Sopenharmony_ci			(*tpd)->word1 |= 1 << TPD_LSO_VER_SHIFT;
20358c2ecf20Sopenharmony_ci		}
20368c2ecf20Sopenharmony_ci
20378c2ecf20Sopenharmony_ci		(*tpd)->word1 |= 1 << TPD_LSO_EN_SHIFT;
20388c2ecf20Sopenharmony_ci		(*tpd)->word1 |= (skb_transport_offset(skb) & TPD_TCPHDR_OFFSET_MASK) <<
20398c2ecf20Sopenharmony_ci				TPD_TCPHDR_OFFSET_SHIFT;
20408c2ecf20Sopenharmony_ci		(*tpd)->word1 |= (skb_shinfo(skb)->gso_size & TPD_MSS_MASK) <<
20418c2ecf20Sopenharmony_ci				TPD_MSS_SHIFT;
20428c2ecf20Sopenharmony_ci		return 0;
20438c2ecf20Sopenharmony_ci	}
20448c2ecf20Sopenharmony_ci
20458c2ecf20Sopenharmony_cicheck_sum:
20468c2ecf20Sopenharmony_ci	if (likely(skb->ip_summed == CHECKSUM_PARTIAL)) {
20478c2ecf20Sopenharmony_ci		u8 css, cso;
20488c2ecf20Sopenharmony_ci		cso = skb_checksum_start_offset(skb);
20498c2ecf20Sopenharmony_ci
20508c2ecf20Sopenharmony_ci		if (unlikely(cso & 0x1)) {
20518c2ecf20Sopenharmony_ci			if (netif_msg_tx_err(adapter))
20528c2ecf20Sopenharmony_ci				dev_err(&adapter->pdev->dev,
20538c2ecf20Sopenharmony_ci					"payload offset should not an event number\n");
20548c2ecf20Sopenharmony_ci			return -1;
20558c2ecf20Sopenharmony_ci		} else {
20568c2ecf20Sopenharmony_ci			css = cso + skb->csum_offset;
20578c2ecf20Sopenharmony_ci
20588c2ecf20Sopenharmony_ci			(*tpd)->word1 |= ((cso >> 1) & TPD_PLOADOFFSET_MASK) <<
20598c2ecf20Sopenharmony_ci					TPD_PLOADOFFSET_SHIFT;
20608c2ecf20Sopenharmony_ci			(*tpd)->word1 |= ((css >> 1) & TPD_CCSUM_OFFSET_MASK) <<
20618c2ecf20Sopenharmony_ci					TPD_CCSUM_OFFSET_SHIFT;
20628c2ecf20Sopenharmony_ci			(*tpd)->word1 |= 1 << TPD_CCSUM_EN_SHIFT;
20638c2ecf20Sopenharmony_ci		}
20648c2ecf20Sopenharmony_ci	}
20658c2ecf20Sopenharmony_ci	return 0;
20668c2ecf20Sopenharmony_ci}
20678c2ecf20Sopenharmony_ci
20688c2ecf20Sopenharmony_cistatic void atl1c_tx_rollback(struct atl1c_adapter *adpt,
20698c2ecf20Sopenharmony_ci			      struct atl1c_tpd_desc *first_tpd,
20708c2ecf20Sopenharmony_ci			      enum atl1c_trans_queue type)
20718c2ecf20Sopenharmony_ci{
20728c2ecf20Sopenharmony_ci	struct atl1c_tpd_ring *tpd_ring = &adpt->tpd_ring[type];
20738c2ecf20Sopenharmony_ci	struct atl1c_buffer *buffer_info;
20748c2ecf20Sopenharmony_ci	struct atl1c_tpd_desc *tpd;
20758c2ecf20Sopenharmony_ci	u16 first_index, index;
20768c2ecf20Sopenharmony_ci
20778c2ecf20Sopenharmony_ci	first_index = first_tpd - (struct atl1c_tpd_desc *)tpd_ring->desc;
20788c2ecf20Sopenharmony_ci	index = first_index;
20798c2ecf20Sopenharmony_ci	while (index != tpd_ring->next_to_use) {
20808c2ecf20Sopenharmony_ci		tpd = ATL1C_TPD_DESC(tpd_ring, index);
20818c2ecf20Sopenharmony_ci		buffer_info = &tpd_ring->buffer_info[index];
20828c2ecf20Sopenharmony_ci		atl1c_clean_buffer(adpt->pdev, buffer_info);
20838c2ecf20Sopenharmony_ci		memset(tpd, 0, sizeof(struct atl1c_tpd_desc));
20848c2ecf20Sopenharmony_ci		if (++index == tpd_ring->count)
20858c2ecf20Sopenharmony_ci			index = 0;
20868c2ecf20Sopenharmony_ci	}
20878c2ecf20Sopenharmony_ci	tpd_ring->next_to_use = first_index;
20888c2ecf20Sopenharmony_ci}
20898c2ecf20Sopenharmony_ci
20908c2ecf20Sopenharmony_cistatic int atl1c_tx_map(struct atl1c_adapter *adapter,
20918c2ecf20Sopenharmony_ci		      struct sk_buff *skb, struct atl1c_tpd_desc *tpd,
20928c2ecf20Sopenharmony_ci			enum atl1c_trans_queue type)
20938c2ecf20Sopenharmony_ci{
20948c2ecf20Sopenharmony_ci	struct atl1c_tpd_desc *use_tpd = NULL;
20958c2ecf20Sopenharmony_ci	struct atl1c_buffer *buffer_info = NULL;
20968c2ecf20Sopenharmony_ci	u16 buf_len = skb_headlen(skb);
20978c2ecf20Sopenharmony_ci	u16 map_len = 0;
20988c2ecf20Sopenharmony_ci	u16 mapped_len = 0;
20998c2ecf20Sopenharmony_ci	u16 hdr_len = 0;
21008c2ecf20Sopenharmony_ci	u16 nr_frags;
21018c2ecf20Sopenharmony_ci	u16 f;
21028c2ecf20Sopenharmony_ci	int tso;
21038c2ecf20Sopenharmony_ci
21048c2ecf20Sopenharmony_ci	nr_frags = skb_shinfo(skb)->nr_frags;
21058c2ecf20Sopenharmony_ci	tso = (tpd->word1 >> TPD_LSO_EN_SHIFT) & TPD_LSO_EN_MASK;
21068c2ecf20Sopenharmony_ci	if (tso) {
21078c2ecf20Sopenharmony_ci		/* TSO */
21088c2ecf20Sopenharmony_ci		map_len = hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb);
21098c2ecf20Sopenharmony_ci		use_tpd = tpd;
21108c2ecf20Sopenharmony_ci
21118c2ecf20Sopenharmony_ci		buffer_info = atl1c_get_tx_buffer(adapter, use_tpd);
21128c2ecf20Sopenharmony_ci		buffer_info->length = map_len;
21138c2ecf20Sopenharmony_ci		buffer_info->dma = dma_map_single(&adapter->pdev->dev,
21148c2ecf20Sopenharmony_ci						  skb->data, hdr_len,
21158c2ecf20Sopenharmony_ci						  DMA_TO_DEVICE);
21168c2ecf20Sopenharmony_ci		if (unlikely(dma_mapping_error(&adapter->pdev->dev, buffer_info->dma)))
21178c2ecf20Sopenharmony_ci			goto err_dma;
21188c2ecf20Sopenharmony_ci		ATL1C_SET_BUFFER_STATE(buffer_info, ATL1C_BUFFER_BUSY);
21198c2ecf20Sopenharmony_ci		ATL1C_SET_PCIMAP_TYPE(buffer_info, ATL1C_PCIMAP_SINGLE,
21208c2ecf20Sopenharmony_ci			ATL1C_PCIMAP_TODEVICE);
21218c2ecf20Sopenharmony_ci		mapped_len += map_len;
21228c2ecf20Sopenharmony_ci		use_tpd->buffer_addr = cpu_to_le64(buffer_info->dma);
21238c2ecf20Sopenharmony_ci		use_tpd->buffer_len = cpu_to_le16(buffer_info->length);
21248c2ecf20Sopenharmony_ci	}
21258c2ecf20Sopenharmony_ci
21268c2ecf20Sopenharmony_ci	if (mapped_len < buf_len) {
21278c2ecf20Sopenharmony_ci		/* mapped_len == 0, means we should use the first tpd,
21288c2ecf20Sopenharmony_ci		   which is given by caller  */
21298c2ecf20Sopenharmony_ci		if (mapped_len == 0)
21308c2ecf20Sopenharmony_ci			use_tpd = tpd;
21318c2ecf20Sopenharmony_ci		else {
21328c2ecf20Sopenharmony_ci			use_tpd = atl1c_get_tpd(adapter, type);
21338c2ecf20Sopenharmony_ci			memcpy(use_tpd, tpd, sizeof(struct atl1c_tpd_desc));
21348c2ecf20Sopenharmony_ci		}
21358c2ecf20Sopenharmony_ci		buffer_info = atl1c_get_tx_buffer(adapter, use_tpd);
21368c2ecf20Sopenharmony_ci		buffer_info->length = buf_len - mapped_len;
21378c2ecf20Sopenharmony_ci		buffer_info->dma =
21388c2ecf20Sopenharmony_ci			dma_map_single(&adapter->pdev->dev,
21398c2ecf20Sopenharmony_ci				       skb->data + mapped_len,
21408c2ecf20Sopenharmony_ci				       buffer_info->length, DMA_TO_DEVICE);
21418c2ecf20Sopenharmony_ci		if (unlikely(dma_mapping_error(&adapter->pdev->dev, buffer_info->dma)))
21428c2ecf20Sopenharmony_ci			goto err_dma;
21438c2ecf20Sopenharmony_ci
21448c2ecf20Sopenharmony_ci		ATL1C_SET_BUFFER_STATE(buffer_info, ATL1C_BUFFER_BUSY);
21458c2ecf20Sopenharmony_ci		ATL1C_SET_PCIMAP_TYPE(buffer_info, ATL1C_PCIMAP_SINGLE,
21468c2ecf20Sopenharmony_ci			ATL1C_PCIMAP_TODEVICE);
21478c2ecf20Sopenharmony_ci		use_tpd->buffer_addr = cpu_to_le64(buffer_info->dma);
21488c2ecf20Sopenharmony_ci		use_tpd->buffer_len  = cpu_to_le16(buffer_info->length);
21498c2ecf20Sopenharmony_ci	}
21508c2ecf20Sopenharmony_ci
21518c2ecf20Sopenharmony_ci	for (f = 0; f < nr_frags; f++) {
21528c2ecf20Sopenharmony_ci		skb_frag_t *frag = &skb_shinfo(skb)->frags[f];
21538c2ecf20Sopenharmony_ci
21548c2ecf20Sopenharmony_ci		use_tpd = atl1c_get_tpd(adapter, type);
21558c2ecf20Sopenharmony_ci		memcpy(use_tpd, tpd, sizeof(struct atl1c_tpd_desc));
21568c2ecf20Sopenharmony_ci
21578c2ecf20Sopenharmony_ci		buffer_info = atl1c_get_tx_buffer(adapter, use_tpd);
21588c2ecf20Sopenharmony_ci		buffer_info->length = skb_frag_size(frag);
21598c2ecf20Sopenharmony_ci		buffer_info->dma = skb_frag_dma_map(&adapter->pdev->dev,
21608c2ecf20Sopenharmony_ci						    frag, 0,
21618c2ecf20Sopenharmony_ci						    buffer_info->length,
21628c2ecf20Sopenharmony_ci						    DMA_TO_DEVICE);
21638c2ecf20Sopenharmony_ci		if (dma_mapping_error(&adapter->pdev->dev, buffer_info->dma))
21648c2ecf20Sopenharmony_ci			goto err_dma;
21658c2ecf20Sopenharmony_ci
21668c2ecf20Sopenharmony_ci		ATL1C_SET_BUFFER_STATE(buffer_info, ATL1C_BUFFER_BUSY);
21678c2ecf20Sopenharmony_ci		ATL1C_SET_PCIMAP_TYPE(buffer_info, ATL1C_PCIMAP_PAGE,
21688c2ecf20Sopenharmony_ci			ATL1C_PCIMAP_TODEVICE);
21698c2ecf20Sopenharmony_ci		use_tpd->buffer_addr = cpu_to_le64(buffer_info->dma);
21708c2ecf20Sopenharmony_ci		use_tpd->buffer_len  = cpu_to_le16(buffer_info->length);
21718c2ecf20Sopenharmony_ci	}
21728c2ecf20Sopenharmony_ci
21738c2ecf20Sopenharmony_ci	/* The last tpd */
21748c2ecf20Sopenharmony_ci	use_tpd->word1 |= 1 << TPD_EOP_SHIFT;
21758c2ecf20Sopenharmony_ci	/* The last buffer info contain the skb address,
21768c2ecf20Sopenharmony_ci	   so it will be free after unmap */
21778c2ecf20Sopenharmony_ci	buffer_info->skb = skb;
21788c2ecf20Sopenharmony_ci
21798c2ecf20Sopenharmony_ci	return 0;
21808c2ecf20Sopenharmony_ci
21818c2ecf20Sopenharmony_cierr_dma:
21828c2ecf20Sopenharmony_ci	buffer_info->dma = 0;
21838c2ecf20Sopenharmony_ci	buffer_info->length = 0;
21848c2ecf20Sopenharmony_ci	return -1;
21858c2ecf20Sopenharmony_ci}
21868c2ecf20Sopenharmony_ci
21878c2ecf20Sopenharmony_cistatic void atl1c_tx_queue(struct atl1c_adapter *adapter, struct sk_buff *skb,
21888c2ecf20Sopenharmony_ci			   struct atl1c_tpd_desc *tpd, enum atl1c_trans_queue type)
21898c2ecf20Sopenharmony_ci{
21908c2ecf20Sopenharmony_ci	struct atl1c_tpd_ring *tpd_ring = &adapter->tpd_ring[type];
21918c2ecf20Sopenharmony_ci	u16 reg;
21928c2ecf20Sopenharmony_ci
21938c2ecf20Sopenharmony_ci	reg = type == atl1c_trans_high ? REG_TPD_PRI1_PIDX : REG_TPD_PRI0_PIDX;
21948c2ecf20Sopenharmony_ci	AT_WRITE_REGW(&adapter->hw, reg, tpd_ring->next_to_use);
21958c2ecf20Sopenharmony_ci}
21968c2ecf20Sopenharmony_ci
21978c2ecf20Sopenharmony_cistatic netdev_tx_t atl1c_xmit_frame(struct sk_buff *skb,
21988c2ecf20Sopenharmony_ci					  struct net_device *netdev)
21998c2ecf20Sopenharmony_ci{
22008c2ecf20Sopenharmony_ci	struct atl1c_adapter *adapter = netdev_priv(netdev);
22018c2ecf20Sopenharmony_ci	u16 tpd_req;
22028c2ecf20Sopenharmony_ci	struct atl1c_tpd_desc *tpd;
22038c2ecf20Sopenharmony_ci	enum atl1c_trans_queue type = atl1c_trans_normal;
22048c2ecf20Sopenharmony_ci
22058c2ecf20Sopenharmony_ci	if (test_bit(__AT_DOWN, &adapter->flags)) {
22068c2ecf20Sopenharmony_ci		dev_kfree_skb_any(skb);
22078c2ecf20Sopenharmony_ci		return NETDEV_TX_OK;
22088c2ecf20Sopenharmony_ci	}
22098c2ecf20Sopenharmony_ci
22108c2ecf20Sopenharmony_ci	tpd_req = atl1c_cal_tpd_req(skb);
22118c2ecf20Sopenharmony_ci
22128c2ecf20Sopenharmony_ci	if (atl1c_tpd_avail(adapter, type) < tpd_req) {
22138c2ecf20Sopenharmony_ci		/* no enough descriptor, just stop queue */
22148c2ecf20Sopenharmony_ci		netif_stop_queue(netdev);
22158c2ecf20Sopenharmony_ci		return NETDEV_TX_BUSY;
22168c2ecf20Sopenharmony_ci	}
22178c2ecf20Sopenharmony_ci
22188c2ecf20Sopenharmony_ci	tpd = atl1c_get_tpd(adapter, type);
22198c2ecf20Sopenharmony_ci
22208c2ecf20Sopenharmony_ci	/* do TSO and check sum */
22218c2ecf20Sopenharmony_ci	if (atl1c_tso_csum(adapter, skb, &tpd, type) != 0) {
22228c2ecf20Sopenharmony_ci		dev_kfree_skb_any(skb);
22238c2ecf20Sopenharmony_ci		return NETDEV_TX_OK;
22248c2ecf20Sopenharmony_ci	}
22258c2ecf20Sopenharmony_ci
22268c2ecf20Sopenharmony_ci	if (unlikely(skb_vlan_tag_present(skb))) {
22278c2ecf20Sopenharmony_ci		u16 vlan = skb_vlan_tag_get(skb);
22288c2ecf20Sopenharmony_ci		__le16 tag;
22298c2ecf20Sopenharmony_ci
22308c2ecf20Sopenharmony_ci		vlan = cpu_to_le16(vlan);
22318c2ecf20Sopenharmony_ci		AT_VLAN_TO_TAG(vlan, tag);
22328c2ecf20Sopenharmony_ci		tpd->word1 |= 1 << TPD_INS_VTAG_SHIFT;
22338c2ecf20Sopenharmony_ci		tpd->vlan_tag = tag;
22348c2ecf20Sopenharmony_ci	}
22358c2ecf20Sopenharmony_ci
22368c2ecf20Sopenharmony_ci	if (skb_network_offset(skb) != ETH_HLEN)
22378c2ecf20Sopenharmony_ci		tpd->word1 |= 1 << TPD_ETH_TYPE_SHIFT; /* Ethernet frame */
22388c2ecf20Sopenharmony_ci
22398c2ecf20Sopenharmony_ci	if (atl1c_tx_map(adapter, skb, tpd, type) < 0) {
22408c2ecf20Sopenharmony_ci		netif_info(adapter, tx_done, adapter->netdev,
22418c2ecf20Sopenharmony_ci			   "tx-skb dropped due to dma error\n");
22428c2ecf20Sopenharmony_ci		/* roll back tpd/buffer */
22438c2ecf20Sopenharmony_ci		atl1c_tx_rollback(adapter, tpd, type);
22448c2ecf20Sopenharmony_ci		dev_kfree_skb_any(skb);
22458c2ecf20Sopenharmony_ci	} else {
22468c2ecf20Sopenharmony_ci		netdev_sent_queue(adapter->netdev, skb->len);
22478c2ecf20Sopenharmony_ci		atl1c_tx_queue(adapter, skb, tpd, type);
22488c2ecf20Sopenharmony_ci	}
22498c2ecf20Sopenharmony_ci
22508c2ecf20Sopenharmony_ci	return NETDEV_TX_OK;
22518c2ecf20Sopenharmony_ci}
22528c2ecf20Sopenharmony_ci
22538c2ecf20Sopenharmony_cistatic void atl1c_free_irq(struct atl1c_adapter *adapter)
22548c2ecf20Sopenharmony_ci{
22558c2ecf20Sopenharmony_ci	struct net_device *netdev = adapter->netdev;
22568c2ecf20Sopenharmony_ci
22578c2ecf20Sopenharmony_ci	free_irq(adapter->pdev->irq, netdev);
22588c2ecf20Sopenharmony_ci
22598c2ecf20Sopenharmony_ci	if (adapter->have_msi)
22608c2ecf20Sopenharmony_ci		pci_disable_msi(adapter->pdev);
22618c2ecf20Sopenharmony_ci}
22628c2ecf20Sopenharmony_ci
22638c2ecf20Sopenharmony_cistatic int atl1c_request_irq(struct atl1c_adapter *adapter)
22648c2ecf20Sopenharmony_ci{
22658c2ecf20Sopenharmony_ci	struct pci_dev    *pdev   = adapter->pdev;
22668c2ecf20Sopenharmony_ci	struct net_device *netdev = adapter->netdev;
22678c2ecf20Sopenharmony_ci	int flags = 0;
22688c2ecf20Sopenharmony_ci	int err = 0;
22698c2ecf20Sopenharmony_ci
22708c2ecf20Sopenharmony_ci	adapter->have_msi = true;
22718c2ecf20Sopenharmony_ci	err = pci_enable_msi(adapter->pdev);
22728c2ecf20Sopenharmony_ci	if (err) {
22738c2ecf20Sopenharmony_ci		if (netif_msg_ifup(adapter))
22748c2ecf20Sopenharmony_ci			dev_err(&pdev->dev,
22758c2ecf20Sopenharmony_ci				"Unable to allocate MSI interrupt Error: %d\n",
22768c2ecf20Sopenharmony_ci				err);
22778c2ecf20Sopenharmony_ci		adapter->have_msi = false;
22788c2ecf20Sopenharmony_ci	}
22798c2ecf20Sopenharmony_ci
22808c2ecf20Sopenharmony_ci	if (!adapter->have_msi)
22818c2ecf20Sopenharmony_ci		flags |= IRQF_SHARED;
22828c2ecf20Sopenharmony_ci	err = request_irq(adapter->pdev->irq, atl1c_intr, flags,
22838c2ecf20Sopenharmony_ci			netdev->name, netdev);
22848c2ecf20Sopenharmony_ci	if (err) {
22858c2ecf20Sopenharmony_ci		if (netif_msg_ifup(adapter))
22868c2ecf20Sopenharmony_ci			dev_err(&pdev->dev,
22878c2ecf20Sopenharmony_ci				"Unable to allocate interrupt Error: %d\n",
22888c2ecf20Sopenharmony_ci				err);
22898c2ecf20Sopenharmony_ci		if (adapter->have_msi)
22908c2ecf20Sopenharmony_ci			pci_disable_msi(adapter->pdev);
22918c2ecf20Sopenharmony_ci		return err;
22928c2ecf20Sopenharmony_ci	}
22938c2ecf20Sopenharmony_ci	if (netif_msg_ifup(adapter))
22948c2ecf20Sopenharmony_ci		dev_dbg(&pdev->dev, "atl1c_request_irq OK\n");
22958c2ecf20Sopenharmony_ci	return err;
22968c2ecf20Sopenharmony_ci}
22978c2ecf20Sopenharmony_ci
22988c2ecf20Sopenharmony_ci
22998c2ecf20Sopenharmony_cistatic void atl1c_reset_dma_ring(struct atl1c_adapter *adapter)
23008c2ecf20Sopenharmony_ci{
23018c2ecf20Sopenharmony_ci	/* release tx-pending skbs and reset tx/rx ring index */
23028c2ecf20Sopenharmony_ci	atl1c_clean_tx_ring(adapter, atl1c_trans_normal);
23038c2ecf20Sopenharmony_ci	atl1c_clean_tx_ring(adapter, atl1c_trans_high);
23048c2ecf20Sopenharmony_ci	atl1c_clean_rx_ring(adapter);
23058c2ecf20Sopenharmony_ci}
23068c2ecf20Sopenharmony_ci
23078c2ecf20Sopenharmony_cistatic int atl1c_up(struct atl1c_adapter *adapter)
23088c2ecf20Sopenharmony_ci{
23098c2ecf20Sopenharmony_ci	struct net_device *netdev = adapter->netdev;
23108c2ecf20Sopenharmony_ci	int err;
23118c2ecf20Sopenharmony_ci
23128c2ecf20Sopenharmony_ci	netif_carrier_off(netdev);
23138c2ecf20Sopenharmony_ci
23148c2ecf20Sopenharmony_ci	err = atl1c_configure(adapter);
23158c2ecf20Sopenharmony_ci	if (unlikely(err))
23168c2ecf20Sopenharmony_ci		goto err_up;
23178c2ecf20Sopenharmony_ci
23188c2ecf20Sopenharmony_ci	err = atl1c_request_irq(adapter);
23198c2ecf20Sopenharmony_ci	if (unlikely(err))
23208c2ecf20Sopenharmony_ci		goto err_up;
23218c2ecf20Sopenharmony_ci
23228c2ecf20Sopenharmony_ci	atl1c_check_link_status(adapter);
23238c2ecf20Sopenharmony_ci	clear_bit(__AT_DOWN, &adapter->flags);
23248c2ecf20Sopenharmony_ci	napi_enable(&adapter->napi);
23258c2ecf20Sopenharmony_ci	atl1c_irq_enable(adapter);
23268c2ecf20Sopenharmony_ci	netif_start_queue(netdev);
23278c2ecf20Sopenharmony_ci	return err;
23288c2ecf20Sopenharmony_ci
23298c2ecf20Sopenharmony_cierr_up:
23308c2ecf20Sopenharmony_ci	atl1c_clean_rx_ring(adapter);
23318c2ecf20Sopenharmony_ci	return err;
23328c2ecf20Sopenharmony_ci}
23338c2ecf20Sopenharmony_ci
23348c2ecf20Sopenharmony_cistatic void atl1c_down(struct atl1c_adapter *adapter)
23358c2ecf20Sopenharmony_ci{
23368c2ecf20Sopenharmony_ci	struct net_device *netdev = adapter->netdev;
23378c2ecf20Sopenharmony_ci
23388c2ecf20Sopenharmony_ci	atl1c_del_timer(adapter);
23398c2ecf20Sopenharmony_ci	adapter->work_event = 0; /* clear all event */
23408c2ecf20Sopenharmony_ci	/* signal that we're down so the interrupt handler does not
23418c2ecf20Sopenharmony_ci	 * reschedule our watchdog timer */
23428c2ecf20Sopenharmony_ci	set_bit(__AT_DOWN, &adapter->flags);
23438c2ecf20Sopenharmony_ci	netif_carrier_off(netdev);
23448c2ecf20Sopenharmony_ci	napi_disable(&adapter->napi);
23458c2ecf20Sopenharmony_ci	atl1c_irq_disable(adapter);
23468c2ecf20Sopenharmony_ci	atl1c_free_irq(adapter);
23478c2ecf20Sopenharmony_ci	/* disable ASPM if device inactive */
23488c2ecf20Sopenharmony_ci	atl1c_disable_l0s_l1(&adapter->hw);
23498c2ecf20Sopenharmony_ci	/* reset MAC to disable all RX/TX */
23508c2ecf20Sopenharmony_ci	atl1c_reset_mac(&adapter->hw);
23518c2ecf20Sopenharmony_ci	msleep(1);
23528c2ecf20Sopenharmony_ci
23538c2ecf20Sopenharmony_ci	adapter->link_speed = SPEED_0;
23548c2ecf20Sopenharmony_ci	adapter->link_duplex = -1;
23558c2ecf20Sopenharmony_ci	atl1c_reset_dma_ring(adapter);
23568c2ecf20Sopenharmony_ci}
23578c2ecf20Sopenharmony_ci
23588c2ecf20Sopenharmony_ci/**
23598c2ecf20Sopenharmony_ci * atl1c_open - Called when a network interface is made active
23608c2ecf20Sopenharmony_ci * @netdev: network interface device structure
23618c2ecf20Sopenharmony_ci *
23628c2ecf20Sopenharmony_ci * Returns 0 on success, negative value on failure
23638c2ecf20Sopenharmony_ci *
23648c2ecf20Sopenharmony_ci * The open entry point is called when a network interface is made
23658c2ecf20Sopenharmony_ci * active by the system (IFF_UP).  At this point all resources needed
23668c2ecf20Sopenharmony_ci * for transmit and receive operations are allocated, the interrupt
23678c2ecf20Sopenharmony_ci * handler is registered with the OS, the watchdog timer is started,
23688c2ecf20Sopenharmony_ci * and the stack is notified that the interface is ready.
23698c2ecf20Sopenharmony_ci */
23708c2ecf20Sopenharmony_cistatic int atl1c_open(struct net_device *netdev)
23718c2ecf20Sopenharmony_ci{
23728c2ecf20Sopenharmony_ci	struct atl1c_adapter *adapter = netdev_priv(netdev);
23738c2ecf20Sopenharmony_ci	int err;
23748c2ecf20Sopenharmony_ci
23758c2ecf20Sopenharmony_ci	/* disallow open during test */
23768c2ecf20Sopenharmony_ci	if (test_bit(__AT_TESTING, &adapter->flags))
23778c2ecf20Sopenharmony_ci		return -EBUSY;
23788c2ecf20Sopenharmony_ci
23798c2ecf20Sopenharmony_ci	/* allocate rx/tx dma buffer & descriptors */
23808c2ecf20Sopenharmony_ci	err = atl1c_setup_ring_resources(adapter);
23818c2ecf20Sopenharmony_ci	if (unlikely(err))
23828c2ecf20Sopenharmony_ci		return err;
23838c2ecf20Sopenharmony_ci
23848c2ecf20Sopenharmony_ci	err = atl1c_up(adapter);
23858c2ecf20Sopenharmony_ci	if (unlikely(err))
23868c2ecf20Sopenharmony_ci		goto err_up;
23878c2ecf20Sopenharmony_ci
23888c2ecf20Sopenharmony_ci	return 0;
23898c2ecf20Sopenharmony_ci
23908c2ecf20Sopenharmony_cierr_up:
23918c2ecf20Sopenharmony_ci	atl1c_free_irq(adapter);
23928c2ecf20Sopenharmony_ci	atl1c_free_ring_resources(adapter);
23938c2ecf20Sopenharmony_ci	atl1c_reset_mac(&adapter->hw);
23948c2ecf20Sopenharmony_ci	return err;
23958c2ecf20Sopenharmony_ci}
23968c2ecf20Sopenharmony_ci
23978c2ecf20Sopenharmony_ci/**
23988c2ecf20Sopenharmony_ci * atl1c_close - Disables a network interface
23998c2ecf20Sopenharmony_ci * @netdev: network interface device structure
24008c2ecf20Sopenharmony_ci *
24018c2ecf20Sopenharmony_ci * Returns 0, this is not allowed to fail
24028c2ecf20Sopenharmony_ci *
24038c2ecf20Sopenharmony_ci * The close entry point is called when an interface is de-activated
24048c2ecf20Sopenharmony_ci * by the OS.  The hardware is still under the drivers control, but
24058c2ecf20Sopenharmony_ci * needs to be disabled.  A global MAC reset is issued to stop the
24068c2ecf20Sopenharmony_ci * hardware, and all transmit and receive resources are freed.
24078c2ecf20Sopenharmony_ci */
24088c2ecf20Sopenharmony_cistatic int atl1c_close(struct net_device *netdev)
24098c2ecf20Sopenharmony_ci{
24108c2ecf20Sopenharmony_ci	struct atl1c_adapter *adapter = netdev_priv(netdev);
24118c2ecf20Sopenharmony_ci
24128c2ecf20Sopenharmony_ci	WARN_ON(test_bit(__AT_RESETTING, &adapter->flags));
24138c2ecf20Sopenharmony_ci	set_bit(__AT_DOWN, &adapter->flags);
24148c2ecf20Sopenharmony_ci	cancel_work_sync(&adapter->common_task);
24158c2ecf20Sopenharmony_ci	atl1c_down(adapter);
24168c2ecf20Sopenharmony_ci	atl1c_free_ring_resources(adapter);
24178c2ecf20Sopenharmony_ci	return 0;
24188c2ecf20Sopenharmony_ci}
24198c2ecf20Sopenharmony_ci
24208c2ecf20Sopenharmony_cistatic int atl1c_suspend(struct device *dev)
24218c2ecf20Sopenharmony_ci{
24228c2ecf20Sopenharmony_ci	struct net_device *netdev = dev_get_drvdata(dev);
24238c2ecf20Sopenharmony_ci	struct atl1c_adapter *adapter = netdev_priv(netdev);
24248c2ecf20Sopenharmony_ci	struct atl1c_hw *hw = &adapter->hw;
24258c2ecf20Sopenharmony_ci	u32 wufc = adapter->wol;
24268c2ecf20Sopenharmony_ci
24278c2ecf20Sopenharmony_ci	atl1c_disable_l0s_l1(hw);
24288c2ecf20Sopenharmony_ci	if (netif_running(netdev)) {
24298c2ecf20Sopenharmony_ci		WARN_ON(test_bit(__AT_RESETTING, &adapter->flags));
24308c2ecf20Sopenharmony_ci		atl1c_down(adapter);
24318c2ecf20Sopenharmony_ci	}
24328c2ecf20Sopenharmony_ci	netif_device_detach(netdev);
24338c2ecf20Sopenharmony_ci
24348c2ecf20Sopenharmony_ci	if (wufc)
24358c2ecf20Sopenharmony_ci		if (atl1c_phy_to_ps_link(hw) != 0)
24368c2ecf20Sopenharmony_ci			dev_dbg(dev, "phy power saving failed");
24378c2ecf20Sopenharmony_ci
24388c2ecf20Sopenharmony_ci	atl1c_power_saving(hw, wufc);
24398c2ecf20Sopenharmony_ci
24408c2ecf20Sopenharmony_ci	return 0;
24418c2ecf20Sopenharmony_ci}
24428c2ecf20Sopenharmony_ci
24438c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
24448c2ecf20Sopenharmony_cistatic int atl1c_resume(struct device *dev)
24458c2ecf20Sopenharmony_ci{
24468c2ecf20Sopenharmony_ci	struct net_device *netdev = dev_get_drvdata(dev);
24478c2ecf20Sopenharmony_ci	struct atl1c_adapter *adapter = netdev_priv(netdev);
24488c2ecf20Sopenharmony_ci
24498c2ecf20Sopenharmony_ci	AT_WRITE_REG(&adapter->hw, REG_WOL_CTRL, 0);
24508c2ecf20Sopenharmony_ci	atl1c_reset_pcie(&adapter->hw, ATL1C_PCIE_L0S_L1_DISABLE);
24518c2ecf20Sopenharmony_ci
24528c2ecf20Sopenharmony_ci	atl1c_phy_reset(&adapter->hw);
24538c2ecf20Sopenharmony_ci	atl1c_reset_mac(&adapter->hw);
24548c2ecf20Sopenharmony_ci	atl1c_phy_init(&adapter->hw);
24558c2ecf20Sopenharmony_ci
24568c2ecf20Sopenharmony_ci	netif_device_attach(netdev);
24578c2ecf20Sopenharmony_ci	if (netif_running(netdev))
24588c2ecf20Sopenharmony_ci		atl1c_up(adapter);
24598c2ecf20Sopenharmony_ci
24608c2ecf20Sopenharmony_ci	return 0;
24618c2ecf20Sopenharmony_ci}
24628c2ecf20Sopenharmony_ci#endif
24638c2ecf20Sopenharmony_ci
24648c2ecf20Sopenharmony_cistatic void atl1c_shutdown(struct pci_dev *pdev)
24658c2ecf20Sopenharmony_ci{
24668c2ecf20Sopenharmony_ci	struct net_device *netdev = pci_get_drvdata(pdev);
24678c2ecf20Sopenharmony_ci	struct atl1c_adapter *adapter = netdev_priv(netdev);
24688c2ecf20Sopenharmony_ci
24698c2ecf20Sopenharmony_ci	atl1c_suspend(&pdev->dev);
24708c2ecf20Sopenharmony_ci	pci_wake_from_d3(pdev, adapter->wol);
24718c2ecf20Sopenharmony_ci	pci_set_power_state(pdev, PCI_D3hot);
24728c2ecf20Sopenharmony_ci}
24738c2ecf20Sopenharmony_ci
24748c2ecf20Sopenharmony_cistatic const struct net_device_ops atl1c_netdev_ops = {
24758c2ecf20Sopenharmony_ci	.ndo_open		= atl1c_open,
24768c2ecf20Sopenharmony_ci	.ndo_stop		= atl1c_close,
24778c2ecf20Sopenharmony_ci	.ndo_validate_addr	= eth_validate_addr,
24788c2ecf20Sopenharmony_ci	.ndo_start_xmit		= atl1c_xmit_frame,
24798c2ecf20Sopenharmony_ci	.ndo_set_mac_address	= atl1c_set_mac_addr,
24808c2ecf20Sopenharmony_ci	.ndo_set_rx_mode	= atl1c_set_multi,
24818c2ecf20Sopenharmony_ci	.ndo_change_mtu		= atl1c_change_mtu,
24828c2ecf20Sopenharmony_ci	.ndo_fix_features	= atl1c_fix_features,
24838c2ecf20Sopenharmony_ci	.ndo_set_features	= atl1c_set_features,
24848c2ecf20Sopenharmony_ci	.ndo_do_ioctl		= atl1c_ioctl,
24858c2ecf20Sopenharmony_ci	.ndo_tx_timeout		= atl1c_tx_timeout,
24868c2ecf20Sopenharmony_ci	.ndo_get_stats		= atl1c_get_stats,
24878c2ecf20Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER
24888c2ecf20Sopenharmony_ci	.ndo_poll_controller	= atl1c_netpoll,
24898c2ecf20Sopenharmony_ci#endif
24908c2ecf20Sopenharmony_ci};
24918c2ecf20Sopenharmony_ci
24928c2ecf20Sopenharmony_cistatic int atl1c_init_netdev(struct net_device *netdev, struct pci_dev *pdev)
24938c2ecf20Sopenharmony_ci{
24948c2ecf20Sopenharmony_ci	SET_NETDEV_DEV(netdev, &pdev->dev);
24958c2ecf20Sopenharmony_ci	pci_set_drvdata(pdev, netdev);
24968c2ecf20Sopenharmony_ci
24978c2ecf20Sopenharmony_ci	netdev->netdev_ops = &atl1c_netdev_ops;
24988c2ecf20Sopenharmony_ci	netdev->watchdog_timeo = AT_TX_WATCHDOG;
24998c2ecf20Sopenharmony_ci	netdev->min_mtu = ETH_ZLEN - (ETH_HLEN + VLAN_HLEN);
25008c2ecf20Sopenharmony_ci	atl1c_set_ethtool_ops(netdev);
25018c2ecf20Sopenharmony_ci
25028c2ecf20Sopenharmony_ci	/* TODO: add when ready */
25038c2ecf20Sopenharmony_ci	netdev->hw_features =	NETIF_F_SG		|
25048c2ecf20Sopenharmony_ci				NETIF_F_HW_CSUM		|
25058c2ecf20Sopenharmony_ci				NETIF_F_HW_VLAN_CTAG_RX	|
25068c2ecf20Sopenharmony_ci				NETIF_F_TSO		|
25078c2ecf20Sopenharmony_ci				NETIF_F_TSO6;
25088c2ecf20Sopenharmony_ci	netdev->features =	netdev->hw_features	|
25098c2ecf20Sopenharmony_ci				NETIF_F_HW_VLAN_CTAG_TX;
25108c2ecf20Sopenharmony_ci	return 0;
25118c2ecf20Sopenharmony_ci}
25128c2ecf20Sopenharmony_ci
25138c2ecf20Sopenharmony_ci/**
25148c2ecf20Sopenharmony_ci * atl1c_probe - Device Initialization Routine
25158c2ecf20Sopenharmony_ci * @pdev: PCI device information struct
25168c2ecf20Sopenharmony_ci * @ent: entry in atl1c_pci_tbl
25178c2ecf20Sopenharmony_ci *
25188c2ecf20Sopenharmony_ci * Returns 0 on success, negative on failure
25198c2ecf20Sopenharmony_ci *
25208c2ecf20Sopenharmony_ci * atl1c_probe initializes an adapter identified by a pci_dev structure.
25218c2ecf20Sopenharmony_ci * The OS initialization, configuring of the adapter private structure,
25228c2ecf20Sopenharmony_ci * and a hardware reset occur.
25238c2ecf20Sopenharmony_ci */
25248c2ecf20Sopenharmony_cistatic int atl1c_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
25258c2ecf20Sopenharmony_ci{
25268c2ecf20Sopenharmony_ci	struct net_device *netdev;
25278c2ecf20Sopenharmony_ci	struct atl1c_adapter *adapter;
25288c2ecf20Sopenharmony_ci	static int cards_found;
25298c2ecf20Sopenharmony_ci
25308c2ecf20Sopenharmony_ci	int err = 0;
25318c2ecf20Sopenharmony_ci
25328c2ecf20Sopenharmony_ci	/* enable device (incl. PCI PM wakeup and hotplug setup) */
25338c2ecf20Sopenharmony_ci	err = pci_enable_device_mem(pdev);
25348c2ecf20Sopenharmony_ci	if (err) {
25358c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "cannot enable PCI device\n");
25368c2ecf20Sopenharmony_ci		return err;
25378c2ecf20Sopenharmony_ci	}
25388c2ecf20Sopenharmony_ci
25398c2ecf20Sopenharmony_ci	/*
25408c2ecf20Sopenharmony_ci	 * The atl1c chip can DMA to 64-bit addresses, but it uses a single
25418c2ecf20Sopenharmony_ci	 * shared register for the high 32 bits, so only a single, aligned,
25428c2ecf20Sopenharmony_ci	 * 4 GB physical address range can be used at a time.
25438c2ecf20Sopenharmony_ci	 *
25448c2ecf20Sopenharmony_ci	 * Supporting 64-bit DMA on this hardware is more trouble than it's
25458c2ecf20Sopenharmony_ci	 * worth.  It is far easier to limit to 32-bit DMA than update
25468c2ecf20Sopenharmony_ci	 * various kernel subsystems to support the mechanics required by a
25478c2ecf20Sopenharmony_ci	 * fixed-high-32-bit system.
25488c2ecf20Sopenharmony_ci	 */
25498c2ecf20Sopenharmony_ci	err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
25508c2ecf20Sopenharmony_ci	if (err) {
25518c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "No usable DMA configuration,aborting\n");
25528c2ecf20Sopenharmony_ci		goto err_dma;
25538c2ecf20Sopenharmony_ci	}
25548c2ecf20Sopenharmony_ci
25558c2ecf20Sopenharmony_ci	err = pci_request_regions(pdev, atl1c_driver_name);
25568c2ecf20Sopenharmony_ci	if (err) {
25578c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "cannot obtain PCI resources\n");
25588c2ecf20Sopenharmony_ci		goto err_pci_reg;
25598c2ecf20Sopenharmony_ci	}
25608c2ecf20Sopenharmony_ci
25618c2ecf20Sopenharmony_ci	pci_set_master(pdev);
25628c2ecf20Sopenharmony_ci
25638c2ecf20Sopenharmony_ci	netdev = alloc_etherdev(sizeof(struct atl1c_adapter));
25648c2ecf20Sopenharmony_ci	if (netdev == NULL) {
25658c2ecf20Sopenharmony_ci		err = -ENOMEM;
25668c2ecf20Sopenharmony_ci		goto err_alloc_etherdev;
25678c2ecf20Sopenharmony_ci	}
25688c2ecf20Sopenharmony_ci
25698c2ecf20Sopenharmony_ci	err = atl1c_init_netdev(netdev, pdev);
25708c2ecf20Sopenharmony_ci	if (err) {
25718c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "init netdevice failed\n");
25728c2ecf20Sopenharmony_ci		goto err_init_netdev;
25738c2ecf20Sopenharmony_ci	}
25748c2ecf20Sopenharmony_ci	adapter = netdev_priv(netdev);
25758c2ecf20Sopenharmony_ci	adapter->bd_number = cards_found;
25768c2ecf20Sopenharmony_ci	adapter->netdev = netdev;
25778c2ecf20Sopenharmony_ci	adapter->pdev = pdev;
25788c2ecf20Sopenharmony_ci	adapter->hw.adapter = adapter;
25798c2ecf20Sopenharmony_ci	adapter->msg_enable = netif_msg_init(-1, atl1c_default_msg);
25808c2ecf20Sopenharmony_ci	adapter->hw.hw_addr = ioremap(pci_resource_start(pdev, 0), pci_resource_len(pdev, 0));
25818c2ecf20Sopenharmony_ci	if (!adapter->hw.hw_addr) {
25828c2ecf20Sopenharmony_ci		err = -EIO;
25838c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "cannot map device registers\n");
25848c2ecf20Sopenharmony_ci		goto err_ioremap;
25858c2ecf20Sopenharmony_ci	}
25868c2ecf20Sopenharmony_ci
25878c2ecf20Sopenharmony_ci	/* init mii data */
25888c2ecf20Sopenharmony_ci	adapter->mii.dev = netdev;
25898c2ecf20Sopenharmony_ci	adapter->mii.mdio_read  = atl1c_mdio_read;
25908c2ecf20Sopenharmony_ci	adapter->mii.mdio_write = atl1c_mdio_write;
25918c2ecf20Sopenharmony_ci	adapter->mii.phy_id_mask = 0x1f;
25928c2ecf20Sopenharmony_ci	adapter->mii.reg_num_mask = MDIO_CTRL_REG_MASK;
25938c2ecf20Sopenharmony_ci	netif_napi_add(netdev, &adapter->napi, atl1c_clean, 64);
25948c2ecf20Sopenharmony_ci	timer_setup(&adapter->phy_config_timer, atl1c_phy_config, 0);
25958c2ecf20Sopenharmony_ci	/* setup the private structure */
25968c2ecf20Sopenharmony_ci	err = atl1c_sw_init(adapter);
25978c2ecf20Sopenharmony_ci	if (err) {
25988c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "net device private data init failed\n");
25998c2ecf20Sopenharmony_ci		goto err_sw_init;
26008c2ecf20Sopenharmony_ci	}
26018c2ecf20Sopenharmony_ci	/* set max MTU */
26028c2ecf20Sopenharmony_ci	atl1c_set_max_mtu(netdev);
26038c2ecf20Sopenharmony_ci
26048c2ecf20Sopenharmony_ci	atl1c_reset_pcie(&adapter->hw, ATL1C_PCIE_L0S_L1_DISABLE);
26058c2ecf20Sopenharmony_ci
26068c2ecf20Sopenharmony_ci	/* Init GPHY as early as possible due to power saving issue  */
26078c2ecf20Sopenharmony_ci	atl1c_phy_reset(&adapter->hw);
26088c2ecf20Sopenharmony_ci
26098c2ecf20Sopenharmony_ci	err = atl1c_reset_mac(&adapter->hw);
26108c2ecf20Sopenharmony_ci	if (err) {
26118c2ecf20Sopenharmony_ci		err = -EIO;
26128c2ecf20Sopenharmony_ci		goto err_reset;
26138c2ecf20Sopenharmony_ci	}
26148c2ecf20Sopenharmony_ci
26158c2ecf20Sopenharmony_ci	/* reset the controller to
26168c2ecf20Sopenharmony_ci	 * put the device in a known good starting state */
26178c2ecf20Sopenharmony_ci	err = atl1c_phy_init(&adapter->hw);
26188c2ecf20Sopenharmony_ci	if (err) {
26198c2ecf20Sopenharmony_ci		err = -EIO;
26208c2ecf20Sopenharmony_ci		goto err_reset;
26218c2ecf20Sopenharmony_ci	}
26228c2ecf20Sopenharmony_ci	if (atl1c_read_mac_addr(&adapter->hw)) {
26238c2ecf20Sopenharmony_ci		/* got a random MAC address, set NET_ADDR_RANDOM to netdev */
26248c2ecf20Sopenharmony_ci		netdev->addr_assign_type = NET_ADDR_RANDOM;
26258c2ecf20Sopenharmony_ci	}
26268c2ecf20Sopenharmony_ci	memcpy(netdev->dev_addr, adapter->hw.mac_addr, netdev->addr_len);
26278c2ecf20Sopenharmony_ci	if (netif_msg_probe(adapter))
26288c2ecf20Sopenharmony_ci		dev_dbg(&pdev->dev, "mac address : %pM\n",
26298c2ecf20Sopenharmony_ci			adapter->hw.mac_addr);
26308c2ecf20Sopenharmony_ci
26318c2ecf20Sopenharmony_ci	atl1c_hw_set_mac_addr(&adapter->hw, adapter->hw.mac_addr);
26328c2ecf20Sopenharmony_ci	INIT_WORK(&adapter->common_task, atl1c_common_task);
26338c2ecf20Sopenharmony_ci	adapter->work_event = 0;
26348c2ecf20Sopenharmony_ci	err = register_netdev(netdev);
26358c2ecf20Sopenharmony_ci	if (err) {
26368c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "register netdevice failed\n");
26378c2ecf20Sopenharmony_ci		goto err_register;
26388c2ecf20Sopenharmony_ci	}
26398c2ecf20Sopenharmony_ci
26408c2ecf20Sopenharmony_ci	cards_found++;
26418c2ecf20Sopenharmony_ci	return 0;
26428c2ecf20Sopenharmony_ci
26438c2ecf20Sopenharmony_cierr_reset:
26448c2ecf20Sopenharmony_cierr_register:
26458c2ecf20Sopenharmony_cierr_sw_init:
26468c2ecf20Sopenharmony_ci	iounmap(adapter->hw.hw_addr);
26478c2ecf20Sopenharmony_cierr_init_netdev:
26488c2ecf20Sopenharmony_cierr_ioremap:
26498c2ecf20Sopenharmony_ci	free_netdev(netdev);
26508c2ecf20Sopenharmony_cierr_alloc_etherdev:
26518c2ecf20Sopenharmony_ci	pci_release_regions(pdev);
26528c2ecf20Sopenharmony_cierr_pci_reg:
26538c2ecf20Sopenharmony_cierr_dma:
26548c2ecf20Sopenharmony_ci	pci_disable_device(pdev);
26558c2ecf20Sopenharmony_ci	return err;
26568c2ecf20Sopenharmony_ci}
26578c2ecf20Sopenharmony_ci
26588c2ecf20Sopenharmony_ci/**
26598c2ecf20Sopenharmony_ci * atl1c_remove - Device Removal Routine
26608c2ecf20Sopenharmony_ci * @pdev: PCI device information struct
26618c2ecf20Sopenharmony_ci *
26628c2ecf20Sopenharmony_ci * atl1c_remove is called by the PCI subsystem to alert the driver
26638c2ecf20Sopenharmony_ci * that it should release a PCI device.  The could be caused by a
26648c2ecf20Sopenharmony_ci * Hot-Plug event, or because the driver is going to be removed from
26658c2ecf20Sopenharmony_ci * memory.
26668c2ecf20Sopenharmony_ci */
26678c2ecf20Sopenharmony_cistatic void atl1c_remove(struct pci_dev *pdev)
26688c2ecf20Sopenharmony_ci{
26698c2ecf20Sopenharmony_ci	struct net_device *netdev = pci_get_drvdata(pdev);
26708c2ecf20Sopenharmony_ci	struct atl1c_adapter *adapter = netdev_priv(netdev);
26718c2ecf20Sopenharmony_ci
26728c2ecf20Sopenharmony_ci	unregister_netdev(netdev);
26738c2ecf20Sopenharmony_ci	/* restore permanent address */
26748c2ecf20Sopenharmony_ci	atl1c_hw_set_mac_addr(&adapter->hw, adapter->hw.perm_mac_addr);
26758c2ecf20Sopenharmony_ci	atl1c_phy_disable(&adapter->hw);
26768c2ecf20Sopenharmony_ci
26778c2ecf20Sopenharmony_ci	iounmap(adapter->hw.hw_addr);
26788c2ecf20Sopenharmony_ci
26798c2ecf20Sopenharmony_ci	pci_release_regions(pdev);
26808c2ecf20Sopenharmony_ci	pci_disable_device(pdev);
26818c2ecf20Sopenharmony_ci	free_netdev(netdev);
26828c2ecf20Sopenharmony_ci}
26838c2ecf20Sopenharmony_ci
26848c2ecf20Sopenharmony_ci/**
26858c2ecf20Sopenharmony_ci * atl1c_io_error_detected - called when PCI error is detected
26868c2ecf20Sopenharmony_ci * @pdev: Pointer to PCI device
26878c2ecf20Sopenharmony_ci * @state: The current pci connection state
26888c2ecf20Sopenharmony_ci *
26898c2ecf20Sopenharmony_ci * This function is called after a PCI bus error affecting
26908c2ecf20Sopenharmony_ci * this device has been detected.
26918c2ecf20Sopenharmony_ci */
26928c2ecf20Sopenharmony_cistatic pci_ers_result_t atl1c_io_error_detected(struct pci_dev *pdev,
26938c2ecf20Sopenharmony_ci						pci_channel_state_t state)
26948c2ecf20Sopenharmony_ci{
26958c2ecf20Sopenharmony_ci	struct net_device *netdev = pci_get_drvdata(pdev);
26968c2ecf20Sopenharmony_ci	struct atl1c_adapter *adapter = netdev_priv(netdev);
26978c2ecf20Sopenharmony_ci
26988c2ecf20Sopenharmony_ci	netif_device_detach(netdev);
26998c2ecf20Sopenharmony_ci
27008c2ecf20Sopenharmony_ci	if (state == pci_channel_io_perm_failure)
27018c2ecf20Sopenharmony_ci		return PCI_ERS_RESULT_DISCONNECT;
27028c2ecf20Sopenharmony_ci
27038c2ecf20Sopenharmony_ci	if (netif_running(netdev))
27048c2ecf20Sopenharmony_ci		atl1c_down(adapter);
27058c2ecf20Sopenharmony_ci
27068c2ecf20Sopenharmony_ci	pci_disable_device(pdev);
27078c2ecf20Sopenharmony_ci
27088c2ecf20Sopenharmony_ci	/* Request a slot slot reset. */
27098c2ecf20Sopenharmony_ci	return PCI_ERS_RESULT_NEED_RESET;
27108c2ecf20Sopenharmony_ci}
27118c2ecf20Sopenharmony_ci
27128c2ecf20Sopenharmony_ci/**
27138c2ecf20Sopenharmony_ci * atl1c_io_slot_reset - called after the pci bus has been reset.
27148c2ecf20Sopenharmony_ci * @pdev: Pointer to PCI device
27158c2ecf20Sopenharmony_ci *
27168c2ecf20Sopenharmony_ci * Restart the card from scratch, as if from a cold-boot. Implementation
27178c2ecf20Sopenharmony_ci * resembles the first-half of the e1000_resume routine.
27188c2ecf20Sopenharmony_ci */
27198c2ecf20Sopenharmony_cistatic pci_ers_result_t atl1c_io_slot_reset(struct pci_dev *pdev)
27208c2ecf20Sopenharmony_ci{
27218c2ecf20Sopenharmony_ci	struct net_device *netdev = pci_get_drvdata(pdev);
27228c2ecf20Sopenharmony_ci	struct atl1c_adapter *adapter = netdev_priv(netdev);
27238c2ecf20Sopenharmony_ci
27248c2ecf20Sopenharmony_ci	if (pci_enable_device(pdev)) {
27258c2ecf20Sopenharmony_ci		if (netif_msg_hw(adapter))
27268c2ecf20Sopenharmony_ci			dev_err(&pdev->dev,
27278c2ecf20Sopenharmony_ci				"Cannot re-enable PCI device after reset\n");
27288c2ecf20Sopenharmony_ci		return PCI_ERS_RESULT_DISCONNECT;
27298c2ecf20Sopenharmony_ci	}
27308c2ecf20Sopenharmony_ci	pci_set_master(pdev);
27318c2ecf20Sopenharmony_ci
27328c2ecf20Sopenharmony_ci	pci_enable_wake(pdev, PCI_D3hot, 0);
27338c2ecf20Sopenharmony_ci	pci_enable_wake(pdev, PCI_D3cold, 0);
27348c2ecf20Sopenharmony_ci
27358c2ecf20Sopenharmony_ci	atl1c_reset_mac(&adapter->hw);
27368c2ecf20Sopenharmony_ci
27378c2ecf20Sopenharmony_ci	return PCI_ERS_RESULT_RECOVERED;
27388c2ecf20Sopenharmony_ci}
27398c2ecf20Sopenharmony_ci
27408c2ecf20Sopenharmony_ci/**
27418c2ecf20Sopenharmony_ci * atl1c_io_resume - called when traffic can start flowing again.
27428c2ecf20Sopenharmony_ci * @pdev: Pointer to PCI device
27438c2ecf20Sopenharmony_ci *
27448c2ecf20Sopenharmony_ci * This callback is called when the error recovery driver tells us that
27458c2ecf20Sopenharmony_ci * its OK to resume normal operation. Implementation resembles the
27468c2ecf20Sopenharmony_ci * second-half of the atl1c_resume routine.
27478c2ecf20Sopenharmony_ci */
27488c2ecf20Sopenharmony_cistatic void atl1c_io_resume(struct pci_dev *pdev)
27498c2ecf20Sopenharmony_ci{
27508c2ecf20Sopenharmony_ci	struct net_device *netdev = pci_get_drvdata(pdev);
27518c2ecf20Sopenharmony_ci	struct atl1c_adapter *adapter = netdev_priv(netdev);
27528c2ecf20Sopenharmony_ci
27538c2ecf20Sopenharmony_ci	if (netif_running(netdev)) {
27548c2ecf20Sopenharmony_ci		if (atl1c_up(adapter)) {
27558c2ecf20Sopenharmony_ci			if (netif_msg_hw(adapter))
27568c2ecf20Sopenharmony_ci				dev_err(&pdev->dev,
27578c2ecf20Sopenharmony_ci					"Cannot bring device back up after reset\n");
27588c2ecf20Sopenharmony_ci			return;
27598c2ecf20Sopenharmony_ci		}
27608c2ecf20Sopenharmony_ci	}
27618c2ecf20Sopenharmony_ci
27628c2ecf20Sopenharmony_ci	netif_device_attach(netdev);
27638c2ecf20Sopenharmony_ci}
27648c2ecf20Sopenharmony_ci
27658c2ecf20Sopenharmony_cistatic const struct pci_error_handlers atl1c_err_handler = {
27668c2ecf20Sopenharmony_ci	.error_detected = atl1c_io_error_detected,
27678c2ecf20Sopenharmony_ci	.slot_reset = atl1c_io_slot_reset,
27688c2ecf20Sopenharmony_ci	.resume = atl1c_io_resume,
27698c2ecf20Sopenharmony_ci};
27708c2ecf20Sopenharmony_ci
27718c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(atl1c_pm_ops, atl1c_suspend, atl1c_resume);
27728c2ecf20Sopenharmony_ci
27738c2ecf20Sopenharmony_cistatic struct pci_driver atl1c_driver = {
27748c2ecf20Sopenharmony_ci	.name     = atl1c_driver_name,
27758c2ecf20Sopenharmony_ci	.id_table = atl1c_pci_tbl,
27768c2ecf20Sopenharmony_ci	.probe    = atl1c_probe,
27778c2ecf20Sopenharmony_ci	.remove   = atl1c_remove,
27788c2ecf20Sopenharmony_ci	.shutdown = atl1c_shutdown,
27798c2ecf20Sopenharmony_ci	.err_handler = &atl1c_err_handler,
27808c2ecf20Sopenharmony_ci	.driver.pm = &atl1c_pm_ops,
27818c2ecf20Sopenharmony_ci};
27828c2ecf20Sopenharmony_ci
27838c2ecf20Sopenharmony_cimodule_pci_driver(atl1c_driver);
2784