Skip to content
Snippets Groups Projects
atl1c_main.c 81.5 KiB
Newer Older
		offset += roundup(tpd_ring[i].size, 8);
	}
	/* init RFD ring */
	for (i = 0; i < num_rx_queues; i++) {
		rfd_ring[i].dma = ring_header->dma + offset;
		rfd_ring[i].desc = (u8 *) ring_header->desc + offset;
		rfd_ring[i].size = sizeof(struct atl1c_rx_free_desc) *
				rfd_ring[i].count;
		offset += roundup(rfd_ring[i].size, 8);
	}

	/* init RRD ring */
	for (i = 0; i < num_rx_queues; i++) {
		rrd_ring[i].dma = ring_header->dma + offset;
		rrd_ring[i].desc = (u8 *) ring_header->desc + offset;
		rrd_ring[i].size = sizeof(struct atl1c_recv_ret_status) *
				rrd_ring[i].count;
		offset += roundup(rrd_ring[i].size, 8);
	}

	adapter->smb.dma = ring_header->dma + offset;
	adapter->smb.smb = (u8 *)ring_header->desc + offset;
	return 0;

err_nomem:
	kfree(tpd_ring->buffer_info);
	return -ENOMEM;
}

static void atl1c_configure_des_ring(struct atl1c_adapter *adapter)
{
	struct atl1c_hw *hw = &adapter->hw;
	struct atl1c_rfd_ring *rfd_ring = (struct atl1c_rfd_ring *)
				adapter->rfd_ring;
	struct atl1c_rrd_ring *rrd_ring = (struct atl1c_rrd_ring *)
				adapter->rrd_ring;
	struct atl1c_tpd_ring *tpd_ring = (struct atl1c_tpd_ring *)
				adapter->tpd_ring;
	struct atl1c_cmb *cmb = (struct atl1c_cmb *) &adapter->cmb;
	struct atl1c_smb *smb = (struct atl1c_smb *) &adapter->smb;
	int i;

	/* TPD */
	AT_WRITE_REG(hw, REG_TX_BASE_ADDR_HI,
			(u32)((tpd_ring[atl1c_trans_normal].dma &
				AT_DMA_HI_ADDR_MASK) >> 32));
	/* just enable normal priority TX queue */
	AT_WRITE_REG(hw, REG_NTPD_HEAD_ADDR_LO,
			(u32)(tpd_ring[atl1c_trans_normal].dma &
				AT_DMA_LO_ADDR_MASK));
	AT_WRITE_REG(hw, REG_HTPD_HEAD_ADDR_LO,
			(u32)(tpd_ring[atl1c_trans_high].dma &
				AT_DMA_LO_ADDR_MASK));
	AT_WRITE_REG(hw, REG_TPD_RING_SIZE,
			(u32)(tpd_ring[0].count & TPD_RING_SIZE_MASK));


	/* RFD */
	AT_WRITE_REG(hw, REG_RX_BASE_ADDR_HI,
			(u32)((rfd_ring[0].dma & AT_DMA_HI_ADDR_MASK) >> 32));
	for (i = 0; i < adapter->num_rx_queues; i++)
		AT_WRITE_REG(hw, atl1c_rfd_addr_lo_regs[i],
			(u32)(rfd_ring[i].dma & AT_DMA_LO_ADDR_MASK));

	AT_WRITE_REG(hw, REG_RFD_RING_SIZE,
			rfd_ring[0].count & RFD_RING_SIZE_MASK);
	AT_WRITE_REG(hw, REG_RX_BUF_SIZE,
			adapter->rx_buffer_len & RX_BUF_SIZE_MASK);

	/* RRD */
	for (i = 0; i < adapter->num_rx_queues; i++)
		AT_WRITE_REG(hw, atl1c_rrd_addr_lo_regs[i],
			(u32)(rrd_ring[i].dma & AT_DMA_LO_ADDR_MASK));
	AT_WRITE_REG(hw, REG_RRD_RING_SIZE,
			(rrd_ring[0].count & RRD_RING_SIZE_MASK));

	/* CMB */
	AT_WRITE_REG(hw, REG_CMB_BASE_ADDR_LO, cmb->dma & AT_DMA_LO_ADDR_MASK);

	/* SMB */
	AT_WRITE_REG(hw, REG_SMB_BASE_ADDR_HI,
			(u32)((smb->dma & AT_DMA_HI_ADDR_MASK) >> 32));
	AT_WRITE_REG(hw, REG_SMB_BASE_ADDR_LO,
			(u32)(smb->dma & AT_DMA_LO_ADDR_MASK));
	if (hw->nic_type == athr_l2c_b) {
		AT_WRITE_REG(hw, REG_SRAM_RXF_LEN, 0x02a0L);
		AT_WRITE_REG(hw, REG_SRAM_TXF_LEN, 0x0100L);
		AT_WRITE_REG(hw, REG_SRAM_RXF_ADDR, 0x029f0000L);
		AT_WRITE_REG(hw, REG_SRAM_RFD0_INFO, 0x02bf02a0L);
		AT_WRITE_REG(hw, REG_SRAM_TXF_ADDR, 0x03bf02c0L);
		AT_WRITE_REG(hw, REG_SRAM_TRD_ADDR, 0x03df03c0L);
		AT_WRITE_REG(hw, REG_TXF_WATER_MARK, 0);	/* TX watermark, to enter l1 state.*/
		AT_WRITE_REG(hw, REG_RXD_DMA_CTRL, 0);		/* RXD threshold.*/
	}
	if (hw->nic_type == athr_l2c_b || hw->nic_type == athr_l1d_2) {
			/* Power Saving for L2c_B */
		AT_READ_REG(hw, REG_SERDES_LOCK, &data);
		data |= SERDES_MAC_CLK_SLOWDOWN;
		data |= SERDES_PYH_CLK_SLOWDOWN;
		AT_WRITE_REG(hw, REG_SERDES_LOCK, data);
	}
	/* Load all of base address above */
	AT_WRITE_REG(hw, REG_LOAD_PTR, 1);
}

static void atl1c_configure_tx(struct atl1c_adapter *adapter)
{
	struct atl1c_hw *hw = &adapter->hw;
	u32 dev_ctrl_data;
	u32 max_pay_load;
	u16 tx_offload_thresh;
	u32 txq_ctrl_data;
	u32 max_pay_load_data;

	tx_offload_thresh = MAX_TX_OFFLOAD_THRESH;
	AT_WRITE_REG(hw, REG_TX_TSO_OFFLOAD_THRESH,
		(tx_offload_thresh >> 3) & TX_TSO_OFFLOAD_THRESH_MASK);
	AT_READ_REG(hw, REG_DEVICE_CTRL, &dev_ctrl_data);
	max_pay_load  = (dev_ctrl_data >> DEVICE_CTRL_MAX_PAYLOAD_SHIFT) &
			DEVICE_CTRL_MAX_PAYLOAD_MASK;
	hw->dmaw_block = min_t(u32, max_pay_load, hw->dmaw_block);
	max_pay_load  = (dev_ctrl_data >> DEVICE_CTRL_MAX_RREQ_SZ_SHIFT) &
			DEVICE_CTRL_MAX_RREQ_SZ_MASK;
	hw->dmar_block = min_t(u32, max_pay_load, hw->dmar_block);

	txq_ctrl_data = (hw->tpd_burst & TXQ_NUM_TPD_BURST_MASK) <<
			TXQ_NUM_TPD_BURST_SHIFT;
	if (hw->ctrl_flags & ATL1C_TXQ_MODE_ENHANCE)
		txq_ctrl_data |= TXQ_CTRL_ENH_MODE;
	max_pay_load_data = (atl1c_pay_load_size[hw->dmar_block] &
			TXQ_TXF_BURST_NUM_MASK) << TXQ_TXF_BURST_NUM_SHIFT;
	if (hw->nic_type == athr_l2c_b || hw->nic_type == athr_l2c_b2)
		max_pay_load_data >>= 1;
	txq_ctrl_data |= max_pay_load_data;

	AT_WRITE_REG(hw, REG_TXQ_CTRL, txq_ctrl_data);
}

static void atl1c_configure_rx(struct atl1c_adapter *adapter)
{
	struct atl1c_hw *hw = &adapter->hw;
	u32 rxq_ctrl_data;

	rxq_ctrl_data = (hw->rfd_burst & RXQ_RFD_BURST_NUM_MASK) <<
			RXQ_RFD_BURST_NUM_SHIFT;

	if (hw->ctrl_flags & ATL1C_RX_IPV6_CHKSUM)
		rxq_ctrl_data |= IPV6_CHKSUM_CTRL_EN;
	if (hw->rss_type == atl1c_rss_ipv4)
		rxq_ctrl_data |= RSS_HASH_IPV4;
	if (hw->rss_type == atl1c_rss_ipv4_tcp)
		rxq_ctrl_data |= RSS_HASH_IPV4_TCP;
	if (hw->rss_type == atl1c_rss_ipv6)
		rxq_ctrl_data |= RSS_HASH_IPV6;
	if (hw->rss_type == atl1c_rss_ipv6_tcp)
		rxq_ctrl_data |= RSS_HASH_IPV6_TCP;
	if (hw->rss_type != atl1c_rss_disable)
		rxq_ctrl_data |= RRS_HASH_CTRL_EN;

	rxq_ctrl_data |= (hw->rss_mode & RSS_MODE_MASK) <<
			RSS_MODE_SHIFT;
	rxq_ctrl_data |= (hw->rss_hash_bits & RSS_HASH_BITS_MASK) <<
			RSS_HASH_BITS_SHIFT;
	if (hw->ctrl_flags & ATL1C_ASPM_CTRL_MON)
		rxq_ctrl_data |= (ASPM_THRUPUT_LIMIT_1M &
			ASPM_THRUPUT_LIMIT_MASK) << ASPM_THRUPUT_LIMIT_SHIFT;

	AT_WRITE_REG(hw, REG_RXQ_CTRL, rxq_ctrl_data);
}

static void atl1c_configure_rss(struct atl1c_adapter *adapter)
{
	struct atl1c_hw *hw = &adapter->hw;

	AT_WRITE_REG(hw, REG_IDT_TABLE, hw->indirect_tab);
	AT_WRITE_REG(hw, REG_BASE_CPU_NUMBER, hw->base_cpu);
}

static void atl1c_configure_dma(struct atl1c_adapter *adapter)
{
	struct atl1c_hw *hw = &adapter->hw;
	u32 dma_ctrl_data;

	dma_ctrl_data = DMA_CTRL_DMAR_REQ_PRI;
	if (hw->ctrl_flags & ATL1C_CMB_ENABLE)
		dma_ctrl_data |= DMA_CTRL_CMB_EN;
	if (hw->ctrl_flags & ATL1C_SMB_ENABLE)
		dma_ctrl_data |= DMA_CTRL_SMB_EN;
	else
		dma_ctrl_data |= MAC_CTRL_SMB_DIS;

	switch (hw->dma_order) {
	case atl1c_dma_ord_in:
		dma_ctrl_data |= DMA_CTRL_DMAR_IN_ORDER;
		break;
	case atl1c_dma_ord_enh:
		dma_ctrl_data |= DMA_CTRL_DMAR_ENH_ORDER;
		break;
	case atl1c_dma_ord_out:
		dma_ctrl_data |= DMA_CTRL_DMAR_OUT_ORDER;
		break;
	default:
		break;
	}

	dma_ctrl_data |= (((u32)hw->dmar_block) & DMA_CTRL_DMAR_BURST_LEN_MASK)
		<< DMA_CTRL_DMAR_BURST_LEN_SHIFT;
	dma_ctrl_data |= (((u32)hw->dmaw_block) & DMA_CTRL_DMAW_BURST_LEN_MASK)
		<< DMA_CTRL_DMAW_BURST_LEN_SHIFT;
	dma_ctrl_data |= (((u32)hw->dmar_dly_cnt) & DMA_CTRL_DMAR_DLY_CNT_MASK)
		<< DMA_CTRL_DMAR_DLY_CNT_SHIFT;
	dma_ctrl_data |= (((u32)hw->dmaw_dly_cnt) & DMA_CTRL_DMAW_DLY_CNT_MASK)
		<< DMA_CTRL_DMAW_DLY_CNT_SHIFT;

	AT_WRITE_REG(hw, REG_DMA_CTRL, dma_ctrl_data);
}

/*
 * Stop the mac, transmit and receive units
 * hw - Struct containing variables accessed by shared code
 * return : 0  or  idle status (if error)
 */
static int atl1c_stop_mac(struct atl1c_hw *hw)
{
	u32 data;

	AT_READ_REG(hw, REG_RXQ_CTRL, &data);
	data &= ~(RXQ1_CTRL_EN | RXQ2_CTRL_EN |
		  RXQ3_CTRL_EN | RXQ_CTRL_EN);
	AT_WRITE_REG(hw, REG_RXQ_CTRL, data);

	AT_READ_REG(hw, REG_TXQ_CTRL, &data);
	data &= ~TXQ_CTRL_EN;
	AT_WRITE_REG(hw, REG_TWSI_CTRL, data);

	atl1c_wait_until_idle(hw);

	AT_READ_REG(hw, REG_MAC_CTRL, &data);
	data &= ~(MAC_CTRL_TX_EN | MAC_CTRL_RX_EN);
	AT_WRITE_REG(hw, REG_MAC_CTRL, data);

	return (int)atl1c_wait_until_idle(hw);
}

static void atl1c_enable_rx_ctrl(struct atl1c_hw *hw)
{
	u32 data;

	AT_READ_REG(hw, REG_RXQ_CTRL, &data);
	switch (hw->adapter->num_rx_queues) {
	case 4:
		data |= (RXQ3_CTRL_EN | RXQ2_CTRL_EN | RXQ1_CTRL_EN);
		break;
	case 3:
		data |= (RXQ2_CTRL_EN | RXQ1_CTRL_EN);
		break;
	case 2:
		data |= RXQ1_CTRL_EN;
		break;
	default:
		break;
	}
	data |= RXQ_CTRL_EN;
	AT_WRITE_REG(hw, REG_RXQ_CTRL, data);
}

static void atl1c_enable_tx_ctrl(struct atl1c_hw *hw)
{
	u32 data;

	AT_READ_REG(hw, REG_TXQ_CTRL, &data);
	data |= TXQ_CTRL_EN;
	AT_WRITE_REG(hw, REG_TXQ_CTRL, data);
}

/*
 * Reset the transmit and receive units; mask and clear all interrupts.
 * hw - Struct containing variables accessed by shared code
 * return : 0  or  idle status (if error)
 */
static int atl1c_reset_mac(struct atl1c_hw *hw)
{
	struct atl1c_adapter *adapter = (struct atl1c_adapter *)hw->adapter;
	struct pci_dev *pdev = adapter->pdev;
	u32 master_ctrl_data = 0;

	AT_WRITE_REG(hw, REG_IMR, 0);
	AT_WRITE_REG(hw, REG_ISR, ISR_DIS_INT);

	/*
	 * Issue Soft Reset to the MAC.  This will reset the chip's
	 * transmit, receive, DMA.  It will not effect
	 * the current PCI configuration.  The global reset bit is self-
	 * clearing, and should clear within a microsecond.
	 */
	AT_READ_REG(hw, REG_MASTER_CTRL, &master_ctrl_data);
	master_ctrl_data |= MASTER_CTRL_OOB_DIS_OFF;
	AT_WRITE_REGW(hw, REG_MASTER_CTRL, ((master_ctrl_data | MASTER_CTRL_SOFT_RST)
			& 0xFFFF));

	AT_WRITE_FLUSH(hw);
	msleep(10);
	/* Wait at least 10ms for All module to be Idle */

	if (atl1c_wait_until_idle(hw)) {
		dev_err(&pdev->dev,
			"MAC state machine can't be idle since"
			" disabled for 10ms second\n");
		return -1;
	}
	return 0;
}

static void atl1c_disable_l0s_l1(struct atl1c_hw *hw)
{
	u32 pm_ctrl_data;

	AT_READ_REG(hw, REG_PM_CTRL, &pm_ctrl_data);
	pm_ctrl_data &= ~(PM_CTRL_L1_ENTRY_TIMER_MASK <<
			PM_CTRL_L1_ENTRY_TIMER_SHIFT);
	pm_ctrl_data &= ~PM_CTRL_CLK_SWH_L1;
	pm_ctrl_data &= ~PM_CTRL_ASPM_L0S_EN;
	pm_ctrl_data &= ~PM_CTRL_ASPM_L1_EN;
	pm_ctrl_data &= ~PM_CTRL_MAC_ASPM_CHK;
	pm_ctrl_data &= ~PM_CTRL_SERDES_PD_EX_L1;

	pm_ctrl_data |= PM_CTRL_SERDES_BUDS_RX_L1_EN;
	pm_ctrl_data |= PM_CTRL_SERDES_PLL_L1_EN;
	pm_ctrl_data |=	PM_CTRL_SERDES_L1_EN;
	AT_WRITE_REG(hw, REG_PM_CTRL, pm_ctrl_data);
}

/*
 * Set ASPM state.
 * Enable/disable L0s/L1 depend on link state.
 */
static void atl1c_set_aspm(struct atl1c_hw *hw, bool linkup)
{
	u32 pm_ctrl_data;
	u32 link_l1_timer = 0xF;

	AT_READ_REG(hw, REG_PM_CTRL, &pm_ctrl_data);
	AT_READ_REG(hw, REG_LINK_CTRL, &link_ctrl_data);

	pm_ctrl_data &= ~PM_CTRL_SERDES_PD_EX_L1;
	pm_ctrl_data &=  ~(PM_CTRL_L1_ENTRY_TIMER_MASK <<
			PM_CTRL_L1_ENTRY_TIMER_SHIFT);
	pm_ctrl_data &= ~(PM_CTRL_LCKDET_TIMER_MASK <<
			PM_CTRL_LCKDET_TIMER_SHIFT);
	pm_ctrl_data |= AT_LCKDET_TIMER	<< PM_CTRL_LCKDET_TIMER_SHIFT;
	if (hw->nic_type == athr_l2c_b || hw->nic_type == athr_l1d ||
		hw->nic_type == athr_l2c_b2 || hw->nic_type == athr_l1d_2) {
		link_ctrl_data &= ~LINK_CTRL_EXT_SYNC;
		if (!(hw->ctrl_flags & ATL1C_APS_MODE_ENABLE)) {
			if (hw->nic_type == athr_l2c_b && hw->revision_id == L2CB_V10)
				link_ctrl_data |= LINK_CTRL_EXT_SYNC;
		}

		AT_WRITE_REG(hw, REG_LINK_CTRL, link_ctrl_data);

		pm_ctrl_data |= PM_CTRL_RCVR_WT_TIMER;
		pm_ctrl_data &= ~(PM_CTRL_PM_REQ_TIMER_MASK <<
			PM_CTRL_PM_REQ_TIMER_SHIFT);
		pm_ctrl_data |= AT_ASPM_L1_TIMER <<
			PM_CTRL_PM_REQ_TIMER_SHIFT;
		pm_ctrl_data &= ~PM_CTRL_SA_DLY_EN;
		pm_ctrl_data &= ~PM_CTRL_HOTRST;
		pm_ctrl_data |= 1 << PM_CTRL_L1_ENTRY_TIMER_SHIFT;
		pm_ctrl_data |= PM_CTRL_SERDES_PD_EX_L1;
	}
	pm_ctrl_data |= PM_CTRL_MAC_ASPM_CHK;
	if (linkup) {
		pm_ctrl_data &= ~PM_CTRL_ASPM_L1_EN;
		pm_ctrl_data &= ~PM_CTRL_ASPM_L0S_EN;
		if (hw->ctrl_flags & ATL1C_ASPM_L1_SUPPORT)
			pm_ctrl_data |= PM_CTRL_ASPM_L1_EN;
		if (hw->ctrl_flags & ATL1C_ASPM_L0S_SUPPORT)
			pm_ctrl_data |= PM_CTRL_ASPM_L0S_EN;

		if (hw->nic_type == athr_l2c_b || hw->nic_type == athr_l1d ||
			hw->nic_type == athr_l2c_b2 || hw->nic_type == athr_l1d_2) {
			if (hw->nic_type == athr_l2c_b)
				if (!(hw->ctrl_flags & ATL1C_APS_MODE_ENABLE))
					pm_ctrl_data &= ~PM_CTRL_ASPM_L0S_EN;
			pm_ctrl_data &= ~PM_CTRL_SERDES_L1_EN;
			pm_ctrl_data &= ~PM_CTRL_SERDES_PLL_L1_EN;
			pm_ctrl_data &= ~PM_CTRL_SERDES_BUDS_RX_L1_EN;
			pm_ctrl_data |= PM_CTRL_CLK_SWH_L1;
		if (hw->adapter->link_speed == SPEED_100 ||
				hw->adapter->link_speed == SPEED_1000) {
				pm_ctrl_data &=  ~(PM_CTRL_L1_ENTRY_TIMER_MASK <<
					PM_CTRL_L1_ENTRY_TIMER_SHIFT);
				if (hw->nic_type == athr_l2c_b)
					link_l1_timer = 7;
				else if (hw->nic_type == athr_l2c_b2 ||
					hw->nic_type == athr_l1d_2)
					link_l1_timer = 4;
				pm_ctrl_data |= link_l1_timer <<
					PM_CTRL_L1_ENTRY_TIMER_SHIFT;
			}
		} else {
			pm_ctrl_data |= PM_CTRL_SERDES_L1_EN;
			pm_ctrl_data |= PM_CTRL_SERDES_PLL_L1_EN;
			pm_ctrl_data |= PM_CTRL_SERDES_BUDS_RX_L1_EN;
			pm_ctrl_data &= ~PM_CTRL_CLK_SWH_L1;
			pm_ctrl_data &= ~PM_CTRL_ASPM_L0S_EN;
			pm_ctrl_data &= ~PM_CTRL_ASPM_L1_EN;
		pm_ctrl_data &= ~PM_CTRL_SERDES_L1_EN;
		pm_ctrl_data &= ~PM_CTRL_ASPM_L0S_EN;
		pm_ctrl_data &= ~PM_CTRL_SERDES_PLL_L1_EN;
		pm_ctrl_data |= PM_CTRL_CLK_SWH_L1;

		if (hw->ctrl_flags & ATL1C_ASPM_L1_SUPPORT)
			pm_ctrl_data |= PM_CTRL_ASPM_L1_EN;
		else
			pm_ctrl_data &= ~PM_CTRL_ASPM_L1_EN;
	}
	AT_WRITE_REG(hw, REG_PM_CTRL, pm_ctrl_data);
}

static void atl1c_setup_mac_ctrl(struct atl1c_adapter *adapter)
{
	struct atl1c_hw *hw = &adapter->hw;
	struct net_device *netdev = adapter->netdev;
	u32 mac_ctrl_data;

	mac_ctrl_data = MAC_CTRL_TX_EN | MAC_CTRL_RX_EN;
	mac_ctrl_data |= (MAC_CTRL_TX_FLOW | MAC_CTRL_RX_FLOW);

	if (adapter->link_duplex == FULL_DUPLEX) {
		hw->mac_duplex = true;
		mac_ctrl_data |= MAC_CTRL_DUPLX;
	}

	if (adapter->link_speed == SPEED_1000)
		hw->mac_speed = atl1c_mac_speed_1000;
	else
		hw->mac_speed = atl1c_mac_speed_10_100;

	mac_ctrl_data |= (hw->mac_speed & MAC_CTRL_SPEED_MASK) <<
			MAC_CTRL_SPEED_SHIFT;

	mac_ctrl_data |= (MAC_CTRL_ADD_CRC | MAC_CTRL_PAD);
	mac_ctrl_data |= ((hw->preamble_len & MAC_CTRL_PRMLEN_MASK) <<
			MAC_CTRL_PRMLEN_SHIFT);

Jiri Pirko's avatar
Jiri Pirko committed
	__atl1c_vlan_mode(netdev->features, &mac_ctrl_data);

	mac_ctrl_data |= MAC_CTRL_BC_EN;
	if (netdev->flags & IFF_PROMISC)
		mac_ctrl_data |= MAC_CTRL_PROMIS_EN;
	if (netdev->flags & IFF_ALLMULTI)
		mac_ctrl_data |= MAC_CTRL_MC_ALL_EN;

	mac_ctrl_data |= MAC_CTRL_SINGLE_PAUSE_EN;
	if (hw->nic_type == athr_l1d || hw->nic_type == athr_l2c_b2 ||
	    hw->nic_type == athr_l1d_2) {
		mac_ctrl_data |= MAC_CTRL_SPEED_MODE_SW;
		mac_ctrl_data |= MAC_CTRL_HASH_ALG_CRC32;
	}
	AT_WRITE_REG(hw, REG_MAC_CTRL, mac_ctrl_data);
}

/*
 * atl1c_configure - Configure Transmit&Receive Unit after Reset
 * @adapter: board private structure
 *
 * Configure the Tx /Rx unit of the MAC after a reset.
 */
static int atl1c_configure(struct atl1c_adapter *adapter)
{
	struct atl1c_hw *hw = &adapter->hw;
	u32 master_ctrl_data = 0;
	u32 intr_modrt_data;

	/* clear interrupt status */
	AT_WRITE_REG(hw, REG_ISR, 0xFFFFFFFF);
	/*  Clear any WOL status */
	AT_WRITE_REG(hw, REG_WOL_CTRL, 0);
	/* set Interrupt Clear Timer
	 * HW will enable self to assert interrupt event to system after
	 * waiting x-time for software to notify it accept interrupt.
	 */

	data = CLK_GATING_EN_ALL;
	if (hw->ctrl_flags & ATL1C_CLK_GATING_EN) {
		if (hw->nic_type == athr_l2c_b)
			data &= ~CLK_GATING_RXMAC_EN;
	} else
		data = 0;
	AT_WRITE_REG(hw, REG_CLK_GATING_CTRL, data);

	AT_WRITE_REG(hw, REG_INT_RETRIG_TIMER,
		hw->ict & INT_RETRIG_TIMER_MASK);

	atl1c_configure_des_ring(adapter);

	if (hw->ctrl_flags & ATL1C_INTR_MODRT_ENABLE) {
		intr_modrt_data = (hw->tx_imt & IRQ_MODRT_TIMER_MASK) <<
					IRQ_MODRT_TX_TIMER_SHIFT;
		intr_modrt_data |= (hw->rx_imt & IRQ_MODRT_TIMER_MASK) <<
					IRQ_MODRT_RX_TIMER_SHIFT;
		AT_WRITE_REG(hw, REG_IRQ_MODRT_TIMER_INIT, intr_modrt_data);
		master_ctrl_data |=
			MASTER_CTRL_TX_ITIMER_EN | MASTER_CTRL_RX_ITIMER_EN;
	}

	if (hw->ctrl_flags & ATL1C_INTR_CLEAR_ON_READ)
		master_ctrl_data |= MASTER_CTRL_INT_RDCLR;

	master_ctrl_data |= MASTER_CTRL_SA_TIMER_EN;
	AT_WRITE_REG(hw, REG_MASTER_CTRL, master_ctrl_data);

	if (hw->ctrl_flags & ATL1C_CMB_ENABLE) {
		AT_WRITE_REG(hw, REG_CMB_TPD_THRESH,
			hw->cmb_tpd & CMB_TPD_THRESH_MASK);
		AT_WRITE_REG(hw, REG_CMB_TX_TIMER,
			hw->cmb_tx_timer & CMB_TX_TIMER_MASK);
	}

	if (hw->ctrl_flags & ATL1C_SMB_ENABLE)
		AT_WRITE_REG(hw, REG_SMB_STAT_TIMER,
			hw->smb_timer & SMB_STAT_TIMER_MASK);
	/* set MTU */
	AT_WRITE_REG(hw, REG_MTU, hw->max_frame_size + ETH_HLEN +
			VLAN_HLEN + ETH_FCS_LEN);
	/* HDS, disable */
	AT_WRITE_REG(hw, REG_HDS_CTRL, 0);

	atl1c_configure_tx(adapter);
	atl1c_configure_rx(adapter);
	atl1c_configure_rss(adapter);
	atl1c_configure_dma(adapter);

	return 0;
}

static void atl1c_update_hw_stats(struct atl1c_adapter *adapter)
{
	u16 hw_reg_addr = 0;
	unsigned long *stats_item = NULL;
	u32 data;

	/* update rx status */
	hw_reg_addr = REG_MAC_RX_STATUS_BIN;
	stats_item  = &adapter->hw_stats.rx_ok;
	while (hw_reg_addr <= REG_MAC_RX_STATUS_END) {
		AT_READ_REG(&adapter->hw, hw_reg_addr, &data);
		*stats_item += data;
		stats_item++;
		hw_reg_addr += 4;
	}
/* update tx status */
	hw_reg_addr = REG_MAC_TX_STATUS_BIN;
	stats_item  = &adapter->hw_stats.tx_ok;
	while (hw_reg_addr <= REG_MAC_TX_STATUS_END) {
		AT_READ_REG(&adapter->hw, hw_reg_addr, &data);
		*stats_item += data;
		stats_item++;
		hw_reg_addr += 4;
	}
}

/*
 * atl1c_get_stats - Get System Network Statistics
 * @netdev: network interface device structure
 *
 * Returns the address of the device statistics structure.
 * The statistics are actually updated from the timer callback.
 */
static struct net_device_stats *atl1c_get_stats(struct net_device *netdev)
{
	struct atl1c_adapter *adapter = netdev_priv(netdev);
	struct atl1c_hw_stats  *hw_stats = &adapter->hw_stats;
	struct net_device_stats *net_stats = &netdev->stats;

	atl1c_update_hw_stats(adapter);
	net_stats->rx_packets = hw_stats->rx_ok;
	net_stats->tx_packets = hw_stats->tx_ok;
	net_stats->rx_bytes   = hw_stats->rx_byte_cnt;
	net_stats->tx_bytes   = hw_stats->tx_byte_cnt;
	net_stats->multicast  = hw_stats->rx_mcast;
	net_stats->collisions = hw_stats->tx_1_col +
				hw_stats->tx_2_col * 2 +
				hw_stats->tx_late_col + hw_stats->tx_abort_col;
	net_stats->rx_errors  = hw_stats->rx_frag + hw_stats->rx_fcs_err +
				hw_stats->rx_len_err + hw_stats->rx_sz_ov +
				hw_stats->rx_rrd_ov + hw_stats->rx_align_err;
	net_stats->rx_fifo_errors   = hw_stats->rx_rxf_ov;
	net_stats->rx_length_errors = hw_stats->rx_len_err;
	net_stats->rx_crc_errors    = hw_stats->rx_fcs_err;
	net_stats->rx_frame_errors  = hw_stats->rx_align_err;
	net_stats->rx_over_errors   = hw_stats->rx_rrd_ov + hw_stats->rx_rxf_ov;

	net_stats->rx_missed_errors = hw_stats->rx_rrd_ov + hw_stats->rx_rxf_ov;

	net_stats->tx_errors = hw_stats->tx_late_col + hw_stats->tx_abort_col +
				hw_stats->tx_underrun + hw_stats->tx_trunc;
	net_stats->tx_fifo_errors    = hw_stats->tx_underrun;
	net_stats->tx_aborted_errors = hw_stats->tx_abort_col;
	net_stats->tx_window_errors  = hw_stats->tx_late_col;

}

static inline void atl1c_clear_phy_int(struct atl1c_adapter *adapter)
{
	u16 phy_data;

	spin_lock(&adapter->mdio_lock);
	atl1c_read_phy_reg(&adapter->hw, MII_ISR, &phy_data);
	spin_unlock(&adapter->mdio_lock);
}

static bool atl1c_clean_tx_irq(struct atl1c_adapter *adapter,
				enum atl1c_trans_queue type)
{
	struct atl1c_tpd_ring *tpd_ring = (struct atl1c_tpd_ring *)
				&adapter->tpd_ring[type];
	struct atl1c_buffer *buffer_info;
	struct pci_dev *pdev = adapter->pdev;
	u16 next_to_clean = atomic_read(&tpd_ring->next_to_clean);
	u16 hw_next_to_clean;
	u16 shift;
	u32 data;

	if (type == atl1c_trans_high)
		shift = MB_HTPD_CONS_IDX_SHIFT;
	else
		shift = MB_NTPD_CONS_IDX_SHIFT;

	AT_READ_REG(&adapter->hw, REG_MB_PRIO_CONS_IDX, &data);
	hw_next_to_clean = (data >> shift) & MB_PRIO_PROD_IDX_MASK;

	while (next_to_clean != hw_next_to_clean) {
		buffer_info = &tpd_ring->buffer_info[next_to_clean];
		atl1c_clean_buffer(pdev, buffer_info, 1);
		if (++next_to_clean == tpd_ring->count)
			next_to_clean = 0;
		atomic_set(&tpd_ring->next_to_clean, next_to_clean);
	}

	if (netif_queue_stopped(adapter->netdev) &&
			netif_carrier_ok(adapter->netdev)) {
		netif_wake_queue(adapter->netdev);
	}

	return true;
}

/*
 * atl1c_intr - Interrupt Handler
 * @irq: interrupt number
 * @data: pointer to a network interface device structure
 * @pt_regs: CPU registers structure
 */
static irqreturn_t atl1c_intr(int irq, void *data)
{
	struct net_device *netdev  = data;
	struct atl1c_adapter *adapter = netdev_priv(netdev);
	struct pci_dev *pdev = adapter->pdev;
	struct atl1c_hw *hw = &adapter->hw;
	int max_ints = AT_MAX_INT_WORK;
	int handled = IRQ_NONE;
	u32 status;
	u32 reg_data;

	do {
		AT_READ_REG(hw, REG_ISR, &reg_data);
		status = reg_data & hw->intr_mask;

		if (status == 0 || (status & ISR_DIS_INT) != 0) {
			if (max_ints != AT_MAX_INT_WORK)
				handled = IRQ_HANDLED;
			break;
		}
		/* link event */
		if (status & ISR_GPHY)
			atl1c_clear_phy_int(adapter);
		/* Ack ISR */
		AT_WRITE_REG(hw, REG_ISR, status | ISR_DIS_INT);
		if (status & ISR_RX_PKT) {
			if (likely(napi_schedule_prep(&adapter->napi))) {
				hw->intr_mask &= ~ISR_RX_PKT;
				AT_WRITE_REG(hw, REG_IMR, hw->intr_mask);
				__napi_schedule(&adapter->napi);
			}
		}
		if (status & ISR_TX_PKT)
			atl1c_clean_tx_irq(adapter, atl1c_trans_normal);

		handled = IRQ_HANDLED;
		/* check if PCIE PHY Link down */
		if (status & ISR_ERROR) {
			if (netif_msg_hw(adapter))
				dev_err(&pdev->dev,
					"atl1c hardware error (status = 0x%x)\n",
					status & ISR_ERROR);
			/* reset MAC */
			adapter->work_event |= ATL1C_WORK_EVENT_RESET;
			schedule_work(&adapter->common_task);
		}

		if (status & ISR_OVER)
			if (netif_msg_intr(adapter))
				dev_warn(&pdev->dev,
					"TX/RX overflow (status = 0x%x)\n",
					status & ISR_OVER);

		/* link event */
		if (status & (ISR_GPHY | ISR_MANUAL)) {
			netdev->stats.tx_carrier_errors++;
			atl1c_link_chg_event(adapter);
			break;
		}

	} while (--max_ints > 0);
	/* re-enable Interrupt*/
	AT_WRITE_REG(&adapter->hw, REG_ISR, 0);
	return handled;
}

static inline void atl1c_rx_checksum(struct atl1c_adapter *adapter,
		  struct sk_buff *skb, struct atl1c_recv_ret_status *prrs)
{
	/*
	 * The pid field in RRS in not correct sometimes, so we
	 * cannot figure out if the packet is fragmented or not,
	 * so we tell the KERNEL CHECKSUM_NONE
	 */
	skb_checksum_none_assert(skb);
}

static int atl1c_alloc_rx_buffer(struct atl1c_adapter *adapter, const int ringid)
{
	struct atl1c_rfd_ring *rfd_ring = &adapter->rfd_ring[ringid];
	struct pci_dev *pdev = adapter->pdev;
	struct atl1c_buffer *buffer_info, *next_info;
	struct sk_buff *skb;
	void *vir_addr = NULL;
	u16 num_alloc = 0;
	u16 rfd_next_to_use, next_next;
	struct atl1c_rx_free_desc *rfd_desc;

	next_next = rfd_next_to_use = rfd_ring->next_to_use;
	if (++next_next == rfd_ring->count)
		next_next = 0;
	buffer_info = &rfd_ring->buffer_info[rfd_next_to_use];
	next_info = &rfd_ring->buffer_info[next_next];

	while (next_info->flags & ATL1C_BUFFER_FREE) {
		rfd_desc = ATL1C_RFD_DESC(rfd_ring, rfd_next_to_use);

		skb = dev_alloc_skb(adapter->rx_buffer_len);
		if (unlikely(!skb)) {
			if (netif_msg_rx_err(adapter))
				dev_warn(&pdev->dev, "alloc rx buffer failed\n");
			break;
		}

		/*
		 * Make buffer alignment 2 beyond a 16 byte boundary
		 * this will result in a 16 byte aligned IP header after
		 * the 14 byte MAC header is removed
		 */
		vir_addr = skb->data;
		ATL1C_SET_BUFFER_STATE(buffer_info, ATL1C_BUFFER_BUSY);
		buffer_info->skb = skb;
		buffer_info->length = adapter->rx_buffer_len;
		buffer_info->dma = pci_map_single(pdev, vir_addr,
						buffer_info->length,
						PCI_DMA_FROMDEVICE);
		ATL1C_SET_PCIMAP_TYPE(buffer_info, ATL1C_PCIMAP_SINGLE,
			ATL1C_PCIMAP_FROMDEVICE);
		rfd_desc->buffer_addr = cpu_to_le64(buffer_info->dma);
		rfd_next_to_use = next_next;
		if (++next_next == rfd_ring->count)
			next_next = 0;
		buffer_info = &rfd_ring->buffer_info[rfd_next_to_use];
		next_info = &rfd_ring->buffer_info[next_next];
		num_alloc++;
	}

	if (num_alloc) {
		/* TODO: update mailbox here */
		wmb();
		rfd_ring->next_to_use = rfd_next_to_use;
		AT_WRITE_REG(&adapter->hw, atl1c_rfd_prod_idx_regs[ringid],
			rfd_ring->next_to_use & MB_RFDX_PROD_IDX_MASK);
	}

	return num_alloc;
}

static void atl1c_clean_rrd(struct atl1c_rrd_ring *rrd_ring,
			struct	atl1c_recv_ret_status *rrs, u16 num)
{
	u16 i;
	/* the relationship between rrd and rfd is one map one */
	for (i = 0; i < num; i++, rrs = ATL1C_RRD_DESC(rrd_ring,
					rrd_ring->next_to_clean)) {
		rrs->word3 &= ~RRS_RXD_UPDATED;
		if (++rrd_ring->next_to_clean == rrd_ring->count)
			rrd_ring->next_to_clean = 0;
	}
}

static void atl1c_clean_rfd(struct atl1c_rfd_ring *rfd_ring,
	struct atl1c_recv_ret_status *rrs, u16 num)
{
	u16 i;
	u16 rfd_index;
	struct atl1c_buffer *buffer_info = rfd_ring->buffer_info;

	rfd_index = (rrs->word0 >> RRS_RX_RFD_INDEX_SHIFT) &
			RRS_RX_RFD_INDEX_MASK;
	for (i = 0; i < num; i++) {
		buffer_info[rfd_index].skb = NULL;
		ATL1C_SET_BUFFER_STATE(&buffer_info[rfd_index],
					ATL1C_BUFFER_FREE);
		if (++rfd_index == rfd_ring->count)
			rfd_index = 0;
	}
	rfd_ring->next_to_clean = rfd_index;
}

static void atl1c_clean_rx_irq(struct atl1c_adapter *adapter, u8 que,
		   int *work_done, int work_to_do)
{
	u16 rfd_num, rfd_index;
	u16 count = 0;
	u16 length;
	struct pci_dev *pdev = adapter->pdev;
	struct net_device *netdev  = adapter->netdev;
	struct atl1c_rfd_ring *rfd_ring = &adapter->rfd_ring[que];
	struct atl1c_rrd_ring *rrd_ring = &adapter->rrd_ring[que];
	struct sk_buff *skb;
	struct atl1c_recv_ret_status *rrs;
	struct atl1c_buffer *buffer_info;

	while (1) {
		if (*work_done >= work_to_do)
			break;
		rrs = ATL1C_RRD_DESC(rrd_ring, rrd_ring->next_to_clean);
		if (likely(RRS_RXD_IS_VALID(rrs->word3))) {
			rfd_num = (rrs->word0 >> RRS_RX_RFD_CNT_SHIFT) &
				RRS_RX_RFD_CNT_MASK;
roel kluin's avatar
roel kluin committed
			if (unlikely(rfd_num != 1))
				/* TODO support mul rfd*/
				if (netif_msg_rx_err(adapter))
					dev_warn(&pdev->dev,
						"Multi rfd not support yet!\n");
			goto rrs_checked;
		} else {
			break;
		}
rrs_checked:
		atl1c_clean_rrd(rrd_ring, rrs, rfd_num);
		if (rrs->word3 & (RRS_RX_ERR_SUM | RRS_802_3_LEN_ERR)) {
			atl1c_clean_rfd(rfd_ring, rrs, rfd_num);
				if (netif_msg_rx_err(adapter))
					dev_warn(&pdev->dev,
						"wrong packet! rrs word3 is %x\n",
						rrs->word3);
			continue;
		}

		length = le16_to_cpu((rrs->word3 >> RRS_PKT_SIZE_SHIFT) &
				RRS_PKT_SIZE_MASK);
		/* Good Receive */
		if (likely(rfd_num == 1)) {
			rfd_index = (rrs->word0 >> RRS_RX_RFD_INDEX_SHIFT) &
					RRS_RX_RFD_INDEX_MASK;
			buffer_info = &rfd_ring->buffer_info[rfd_index];
			pci_unmap_single(pdev, buffer_info->dma,
				buffer_info->length, PCI_DMA_FROMDEVICE);
			skb = buffer_info->skb;
		} else {
			/* TODO */
			if (netif_msg_rx_err(adapter))
				dev_warn(&pdev->dev,
					"Multi rfd not support yet!\n");
			break;
		}
		atl1c_clean_rfd(rfd_ring, rrs, rfd_num);
		skb_put(skb, length - ETH_FCS_LEN);
		skb->protocol = eth_type_trans(skb, netdev);
		atl1c_rx_checksum(adapter, skb, rrs);
Jiri Pirko's avatar
Jiri Pirko committed
		if (rrs->word3 & RRS_VLAN_INS) {
			u16 vlan;

			AT_TAG_TO_VLAN(rrs->vlan_tag, vlan);
			vlan = le16_to_cpu(vlan);
Jiri Pirko's avatar
Jiri Pirko committed
			__vlan_hwaccel_put_tag(skb, vlan);
		}
		netif_receive_skb(skb);

		(*work_done)++;
		count++;
	}
	if (count)
		atl1c_alloc_rx_buffer(adapter, que);
}

/*
 * atl1c_clean - NAPI Rx polling callback
 * @adapter: board private structure
 */
static int atl1c_clean(struct napi_struct *napi, int budget)
{
	struct atl1c_adapter *adapter =
			container_of(napi, struct atl1c_adapter, napi);
	int work_done = 0;

	/* Keep link state information with original netdev */
	if (!netif_carrier_ok(adapter->netdev))
		goto quit_polling;
	/* just enable one RXQ */
	atl1c_clean_rx_irq(adapter, 0, &work_done, budget);

	if (work_done < budget) {
quit_polling:
		napi_complete(napi);
		adapter->hw.intr_mask |= ISR_RX_PKT;
		AT_WRITE_REG(&adapter->hw, REG_IMR, adapter->hw.intr_mask);
	}
	return work_done;
}

#ifdef CONFIG_NET_POLL_CONTROLLER

/*
 * Polling 'interrupt' - used by things like netconsole to send skbs
 * without having to re-enable interrupts. It's not called while
 * the interrupt routine is executing.
 */
static void atl1c_netpoll(struct net_device *netdev)
{
	struct atl1c_adapter *adapter = netdev_priv(netdev);

	disable_irq(adapter->pdev->irq);
	atl1c_intr(adapter->pdev->irq, netdev);
	enable_irq(adapter->pdev->irq);
}
#endif

static inline u16 atl1c_tpd_avail(struct atl1c_adapter *adapter, enum atl1c_trans_queue type)
{
	struct atl1c_tpd_ring *tpd_ring = &adapter->tpd_ring[type];
	u16 next_to_use = 0;
	u16 next_to_clean = 0;

	next_to_clean = atomic_read(&tpd_ring->next_to_clean);
	next_to_use   = tpd_ring->next_to_use;

	return (u16)(next_to_clean > next_to_use) ?
		(next_to_clean - next_to_use - 1) :
		(tpd_ring->count + next_to_clean - next_to_use - 1);
}

/*
 * get next usable tpd
 * Note: should call atl1c_tdp_avail to make sure
 * there is enough tpd to use
 */
static struct atl1c_tpd_desc *atl1c_get_tpd(struct atl1c_adapter *adapter,
	enum atl1c_trans_queue type)
{
	struct atl1c_tpd_ring *tpd_ring = &adapter->tpd_ring[type];
	struct atl1c_tpd_desc *tpd_desc;
	u16 next_to_use = 0;

	next_to_use = tpd_ring->next_to_use;
	if (++tpd_ring->next_to_use == tpd_ring->count)
		tpd_ring->next_to_use = 0;
	tpd_desc = ATL1C_TPD_DESC(tpd_ring, next_to_use);
	memset(tpd_desc, 0, sizeof(struct atl1c_tpd_desc));
	return	tpd_desc;
}

static struct atl1c_buffer *
atl1c_get_tx_buffer(struct atl1c_adapter *adapter, struct atl1c_tpd_desc *tpd)
{
	struct atl1c_tpd_ring *tpd_ring = adapter->tpd_ring;

	return &tpd_ring->buffer_info[tpd -
			(struct atl1c_tpd_desc *)tpd_ring->desc];
}