641 lines
17 KiB
C
641 lines
17 KiB
C
|
/* Copyright (c) 2013-2015, The Linux Foundation. All rights reserved.
|
||
|
*
|
||
|
* This program is free software; you can redistribute it and/or modify
|
||
|
* it under the terms of the GNU General Public License version 2 and
|
||
|
* only version 2 as published by the Free Software Foundation.
|
||
|
*
|
||
|
* This program is distributed in the hope that it will be useful,
|
||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
* GNU General Public License for more details.
|
||
|
*/
|
||
|
|
||
|
/* Qualcomm Technologies, Inc. EMAC Ethernet Controller Hardware support
|
||
|
*/
|
||
|
|
||
|
#include <linux/crc32.h>
|
||
|
#include <linux/if_vlan.h>
|
||
|
#include <linux/jiffies.h>
|
||
|
#include <linux/phy.h>
|
||
|
#include <linux/of.h>
|
||
|
|
||
|
#include "emac_hw.h"
|
||
|
#include "emac_ptp.h"
|
||
|
|
||
|
#define RFD_PREF_LOW_TH 0x10
|
||
|
#define RFD_PREF_UP_TH 0x10
|
||
|
#define JUMBO_1KAH 0x4
|
||
|
|
||
|
#define RXF_DOF_TH 0x0be
|
||
|
#define RXF_UOF_TH 0x1a0
|
||
|
|
||
|
#define RXD_TH 0x100
|
||
|
|
||
|
/* RGMII specific macros */
|
||
|
#define EMAC_RGMII_PLL_LOCK_TIMEOUT (HZ / 1000) /* 1ms */
|
||
|
#define EMAC_RGMII_CORE_IE_C 0x2001
|
||
|
#define EMAC_RGMII_PLL_L_VAL 0x14
|
||
|
#define EMAC_RGMII_PHY_MODE 0
|
||
|
|
||
|
/* REG */
|
||
|
u32 emac_reg_r32(struct emac_hw *hw, u8 base, u32 reg)
|
||
|
{
|
||
|
return readl_relaxed(hw->reg_addr[base] + reg);
|
||
|
}
|
||
|
|
||
|
void emac_reg_w32(struct emac_hw *hw, u8 base, u32 reg, u32 val)
|
||
|
{
|
||
|
writel_relaxed(val, hw->reg_addr[base] + reg);
|
||
|
}
|
||
|
|
||
|
void emac_reg_update32(struct emac_hw *hw, u8 base, u32 reg, u32 mask, u32 val)
|
||
|
{
|
||
|
u32 data;
|
||
|
|
||
|
data = emac_reg_r32(hw, base, reg);
|
||
|
emac_reg_w32(hw, base, reg, ((data & ~mask) | val));
|
||
|
}
|
||
|
|
||
|
u32 emac_reg_field_r32(struct emac_hw *hw, u8 base, u32 reg,
|
||
|
u32 mask, u32 shift)
|
||
|
{
|
||
|
u32 data;
|
||
|
|
||
|
data = emac_reg_r32(hw, base, reg);
|
||
|
return (data & mask) >> shift;
|
||
|
}
|
||
|
|
||
|
/* INTR */
|
||
|
void emac_hw_enable_intr(struct emac_hw *hw)
|
||
|
{
|
||
|
struct emac_adapter *adpt = emac_hw_get_adap(hw);
|
||
|
int i;
|
||
|
|
||
|
for (i = 0; i < EMAC_NUM_CORE_IRQ; i++) {
|
||
|
struct emac_irq_per_dev *irq = &adpt->irq[i];
|
||
|
const struct emac_irq_common *irq_cmn = &emac_irq_cmn_tbl[i];
|
||
|
|
||
|
emac_reg_w32(hw, EMAC, irq_cmn->status_reg, ~DIS_INT);
|
||
|
emac_reg_w32(hw, EMAC, irq_cmn->mask_reg, irq->mask);
|
||
|
}
|
||
|
|
||
|
if (adpt->tstamp_en)
|
||
|
emac_reg_w32(hw, EMAC_1588, EMAC_P1588_PTP_EXPANDED_INT_MASK,
|
||
|
hw->ptp_intr_mask);
|
||
|
wmb(); /* ensure that irq and ptp setting are flushed to HW */
|
||
|
}
|
||
|
|
||
|
void emac_hw_disable_intr(struct emac_hw *hw)
|
||
|
{
|
||
|
struct emac_adapter *adpt = emac_hw_get_adap(hw);
|
||
|
int i;
|
||
|
|
||
|
for (i = 0; i < EMAC_NUM_CORE_IRQ; i++) {
|
||
|
const struct emac_irq_common *irq_cmn = &emac_irq_cmn_tbl[i];
|
||
|
|
||
|
emac_reg_w32(hw, EMAC, irq_cmn->status_reg, DIS_INT);
|
||
|
emac_reg_w32(hw, EMAC, irq_cmn->mask_reg, 0);
|
||
|
}
|
||
|
|
||
|
if (adpt->tstamp_en)
|
||
|
emac_reg_w32(hw, EMAC_1588, EMAC_P1588_PTP_EXPANDED_INT_MASK,
|
||
|
0);
|
||
|
|
||
|
wmb(); /* ensure that irq and ptp setting are flushed to HW */
|
||
|
}
|
||
|
|
||
|
/* MC */
|
||
|
void emac_hw_set_mc_addr(struct emac_hw *hw, u8 *addr)
|
||
|
{
|
||
|
u32 crc32, bit, reg, mta;
|
||
|
|
||
|
/* Calculate the CRC of the MAC address */
|
||
|
crc32 = ether_crc(ETH_ALEN, addr);
|
||
|
|
||
|
/* The HASH Table is an array of 2 32-bit registers. It is
|
||
|
* treated like an array of 64 bits (BitArray[hash_value]).
|
||
|
* Use the upper 6 bits of the above CRC as the hash value.
|
||
|
*/
|
||
|
reg = (crc32 >> 31) & 0x1;
|
||
|
bit = (crc32 >> 26) & 0x1F;
|
||
|
|
||
|
mta = emac_reg_r32(hw, EMAC, EMAC_HASH_TAB_REG0 + (reg << 2));
|
||
|
mta |= (0x1 << bit);
|
||
|
emac_reg_w32(hw, EMAC, EMAC_HASH_TAB_REG0 + (reg << 2), mta);
|
||
|
wmb(); /* ensure that the mac address is flushed to HW */
|
||
|
}
|
||
|
|
||
|
void emac_hw_clear_mc_addr(struct emac_hw *hw)
|
||
|
{
|
||
|
emac_reg_w32(hw, EMAC, EMAC_HASH_TAB_REG0, 0);
|
||
|
emac_reg_w32(hw, EMAC, EMAC_HASH_TAB_REG1, 0);
|
||
|
wmb(); /* ensure that clearing the mac address is flushed to HW */
|
||
|
}
|
||
|
|
||
|
/* definitions for RSS */
|
||
|
#define EMAC_RSS_KEY(_i, _type) \
|
||
|
(EMAC_RSS_KEY0 + ((_i) * sizeof(_type)))
|
||
|
#define EMAC_RSS_TBL(_i, _type) \
|
||
|
(EMAC_IDT_TABLE0 + ((_i) * sizeof(_type)))
|
||
|
|
||
|
/* RSS */
|
||
|
void emac_hw_config_rss(struct emac_hw *hw)
|
||
|
{
|
||
|
int key_len_by_u32 = sizeof(hw->rss_key) / sizeof(u32);
|
||
|
int idt_len_by_u32 = sizeof(hw->rss_idt) / sizeof(u32);
|
||
|
u32 rxq0;
|
||
|
int i;
|
||
|
|
||
|
/* Fill out hash function keys */
|
||
|
for (i = 0; i < key_len_by_u32; i++) {
|
||
|
u32 key, idx_base;
|
||
|
|
||
|
idx_base = (key_len_by_u32 - i) * 4;
|
||
|
key = ((hw->rss_key[idx_base - 1]) |
|
||
|
(hw->rss_key[idx_base - 2] << 8) |
|
||
|
(hw->rss_key[idx_base - 3] << 16) |
|
||
|
(hw->rss_key[idx_base - 4] << 24));
|
||
|
emac_reg_w32(hw, EMAC, EMAC_RSS_KEY(i, u32), key);
|
||
|
}
|
||
|
|
||
|
/* Fill out redirection table */
|
||
|
for (i = 0; i < idt_len_by_u32; i++)
|
||
|
emac_reg_w32(hw, EMAC, EMAC_RSS_TBL(i, u32), hw->rss_idt[i]);
|
||
|
|
||
|
emac_reg_w32(hw, EMAC, EMAC_BASE_CPU_NUMBER, hw->rss_base_cpu);
|
||
|
|
||
|
rxq0 = emac_reg_r32(hw, EMAC, EMAC_RXQ_CTRL_0);
|
||
|
if (hw->rss_hstype & EMAC_RSS_HSTYP_IPV4_EN)
|
||
|
rxq0 |= RXQ0_RSS_HSTYP_IPV4_EN;
|
||
|
else
|
||
|
rxq0 &= ~RXQ0_RSS_HSTYP_IPV4_EN;
|
||
|
|
||
|
if (hw->rss_hstype & EMAC_RSS_HSTYP_TCP4_EN)
|
||
|
rxq0 |= RXQ0_RSS_HSTYP_IPV4_TCP_EN;
|
||
|
else
|
||
|
rxq0 &= ~RXQ0_RSS_HSTYP_IPV4_TCP_EN;
|
||
|
|
||
|
if (hw->rss_hstype & EMAC_RSS_HSTYP_IPV6_EN)
|
||
|
rxq0 |= RXQ0_RSS_HSTYP_IPV6_EN;
|
||
|
else
|
||
|
rxq0 &= ~RXQ0_RSS_HSTYP_IPV6_EN;
|
||
|
|
||
|
if (hw->rss_hstype & EMAC_RSS_HSTYP_TCP6_EN)
|
||
|
rxq0 |= RXQ0_RSS_HSTYP_IPV6_TCP_EN;
|
||
|
else
|
||
|
rxq0 &= ~RXQ0_RSS_HSTYP_IPV6_TCP_EN;
|
||
|
|
||
|
rxq0 |= ((hw->rss_idt_size << IDT_TABLE_SIZE_SHFT) &
|
||
|
IDT_TABLE_SIZE_BMSK);
|
||
|
rxq0 |= RSS_HASH_EN;
|
||
|
|
||
|
wmb(); /* ensure all parameters are written before we enable RSS */
|
||
|
emac_reg_w32(hw, EMAC, EMAC_RXQ_CTRL_0, rxq0);
|
||
|
wmb(); /* ensure that enabling RSS is flushed to HW */
|
||
|
}
|
||
|
|
||
|
/* Config MAC modes */
|
||
|
void emac_hw_config_mac_ctrl(struct emac_hw *hw)
|
||
|
{
|
||
|
u32 mac;
|
||
|
|
||
|
mac = emac_reg_r32(hw, EMAC, EMAC_MAC_CTRL);
|
||
|
|
||
|
if (TEST_FLAG(hw, HW_VLANSTRIP_EN))
|
||
|
mac |= VLAN_STRIP;
|
||
|
else
|
||
|
mac &= ~VLAN_STRIP;
|
||
|
|
||
|
if (TEST_FLAG(hw, HW_PROMISC_EN))
|
||
|
mac |= PROM_MODE;
|
||
|
else
|
||
|
mac &= ~PROM_MODE;
|
||
|
|
||
|
if (TEST_FLAG(hw, HW_MULTIALL_EN))
|
||
|
mac |= MULTI_ALL;
|
||
|
else
|
||
|
mac &= ~MULTI_ALL;
|
||
|
|
||
|
if (TEST_FLAG(hw, HW_LOOPBACK_EN))
|
||
|
mac |= MAC_LP_EN;
|
||
|
else
|
||
|
mac &= ~MAC_LP_EN;
|
||
|
|
||
|
emac_reg_w32(hw, EMAC, EMAC_MAC_CTRL, mac);
|
||
|
wmb(); /* ensure MAC setting is flushed to HW */
|
||
|
}
|
||
|
|
||
|
/* Wake On LAN (WOL) */
|
||
|
void emac_hw_config_wol(struct emac_hw *hw, u32 wufc)
|
||
|
{
|
||
|
u32 wol = 0;
|
||
|
|
||
|
/* turn on magic packet event */
|
||
|
if (wufc & EMAC_WOL_MAGIC)
|
||
|
wol |= MG_FRAME_EN | MG_FRAME_PME | WK_FRAME_EN;
|
||
|
|
||
|
/* turn on link up event */
|
||
|
if (wufc & EMAC_WOL_PHY)
|
||
|
wol |= LK_CHG_EN | LK_CHG_PME;
|
||
|
|
||
|
emac_reg_w32(hw, EMAC, EMAC_WOL_CTRL0, wol);
|
||
|
wmb(); /* ensure that WOL setting is flushed to HW */
|
||
|
}
|
||
|
|
||
|
/* Power Management */
|
||
|
void emac_hw_config_pow_save(struct emac_hw *hw, u32 speed,
|
||
|
bool wol_en, bool rx_en)
|
||
|
{
|
||
|
u32 dma_mas, mac;
|
||
|
|
||
|
dma_mas = emac_reg_r32(hw, EMAC, EMAC_DMA_MAS_CTRL);
|
||
|
dma_mas &= ~LPW_CLK_SEL;
|
||
|
dma_mas |= LPW_STATE;
|
||
|
|
||
|
mac = emac_reg_r32(hw, EMAC, EMAC_MAC_CTRL);
|
||
|
mac &= ~(FULLD | RXEN | TXEN);
|
||
|
mac = (mac & ~SPEED_BMSK) |
|
||
|
(((u32)emac_mac_speed_10_100 << SPEED_SHFT) & SPEED_BMSK);
|
||
|
|
||
|
if (wol_en) {
|
||
|
if (rx_en)
|
||
|
mac |= (RXEN | BROAD_EN);
|
||
|
|
||
|
/* If WOL is enabled, set link speed/duplex for mac */
|
||
|
if (EMAC_LINK_SPEED_1GB_FULL == speed)
|
||
|
mac = (mac & ~SPEED_BMSK) |
|
||
|
(((u32)emac_mac_speed_1000 << SPEED_SHFT) &
|
||
|
SPEED_BMSK);
|
||
|
|
||
|
if (EMAC_LINK_SPEED_10_FULL == speed ||
|
||
|
EMAC_LINK_SPEED_100_FULL == speed ||
|
||
|
EMAC_LINK_SPEED_1GB_FULL == speed)
|
||
|
mac |= FULLD;
|
||
|
} else {
|
||
|
/* select lower clock speed if WOL is disabled */
|
||
|
dma_mas |= LPW_CLK_SEL;
|
||
|
}
|
||
|
|
||
|
emac_reg_w32(hw, EMAC, EMAC_DMA_MAS_CTRL, dma_mas);
|
||
|
emac_reg_w32(hw, EMAC, EMAC_MAC_CTRL, mac);
|
||
|
wmb(); /* ensure that power setting is flushed to HW */
|
||
|
}
|
||
|
|
||
|
/* Config descriptor rings */
|
||
|
static void emac_hw_config_ring_ctrl(struct emac_hw *hw)
|
||
|
{
|
||
|
struct emac_adapter *adpt = emac_hw_get_adap(hw);
|
||
|
|
||
|
if (adpt->tstamp_en) {
|
||
|
emac_reg_update32(hw, EMAC_CSR, EMAC_EMAC_WRAPPER_CSR1,
|
||
|
0, ENABLE_RRD_TIMESTAMP);
|
||
|
}
|
||
|
|
||
|
/* TPD */
|
||
|
emac_reg_w32(hw, EMAC, EMAC_DESC_CTRL_1,
|
||
|
EMAC_DMA_ADDR_HI(adpt->tx_queue[0].tpd.tpdma));
|
||
|
switch (adpt->num_txques) {
|
||
|
case 4:
|
||
|
emac_reg_w32(hw, EMAC, EMAC_H3TPD_BASE_ADDR_LO,
|
||
|
EMAC_DMA_ADDR_LO(adpt->tx_queue[3].tpd.tpdma));
|
||
|
/* fall through */
|
||
|
case 3:
|
||
|
emac_reg_w32(hw, EMAC, EMAC_H2TPD_BASE_ADDR_LO,
|
||
|
EMAC_DMA_ADDR_LO(adpt->tx_queue[2].tpd.tpdma));
|
||
|
/* fall through */
|
||
|
case 2:
|
||
|
emac_reg_w32(hw, EMAC, EMAC_H1TPD_BASE_ADDR_LO,
|
||
|
EMAC_DMA_ADDR_LO(adpt->tx_queue[1].tpd.tpdma));
|
||
|
/* fall through */
|
||
|
case 1:
|
||
|
emac_reg_w32(hw, EMAC, EMAC_DESC_CTRL_8,
|
||
|
EMAC_DMA_ADDR_LO(adpt->tx_queue[0].tpd.tpdma));
|
||
|
break;
|
||
|
default:
|
||
|
emac_err(adpt, "Invalid number of TX queues (%d)\n",
|
||
|
adpt->num_txques);
|
||
|
return;
|
||
|
}
|
||
|
emac_reg_w32(hw, EMAC, EMAC_DESC_CTRL_9,
|
||
|
adpt->tx_queue[0].tpd.count & TPD_RING_SIZE_BMSK);
|
||
|
|
||
|
/* RFD & RRD */
|
||
|
emac_reg_w32(hw, EMAC, EMAC_DESC_CTRL_0,
|
||
|
EMAC_DMA_ADDR_HI(adpt->rx_queue[0].rfd.rfdma));
|
||
|
switch (adpt->num_rxques) {
|
||
|
case 4:
|
||
|
emac_reg_w32(hw, EMAC, EMAC_DESC_CTRL_13,
|
||
|
EMAC_DMA_ADDR_LO(adpt->rx_queue[3].rfd.rfdma));
|
||
|
emac_reg_w32(hw, EMAC, EMAC_DESC_CTRL_16,
|
||
|
EMAC_DMA_ADDR_LO(adpt->rx_queue[3].rrd.rrdma));
|
||
|
/* fall through */
|
||
|
case 3:
|
||
|
emac_reg_w32(hw, EMAC, EMAC_DESC_CTRL_12,
|
||
|
EMAC_DMA_ADDR_LO(adpt->rx_queue[2].rfd.rfdma));
|
||
|
emac_reg_w32(hw, EMAC, EMAC_DESC_CTRL_15,
|
||
|
EMAC_DMA_ADDR_LO(adpt->rx_queue[2].rrd.rrdma));
|
||
|
/* fall through */
|
||
|
case 2:
|
||
|
emac_reg_w32(hw, EMAC, EMAC_DESC_CTRL_10,
|
||
|
EMAC_DMA_ADDR_LO(adpt->rx_queue[1].rfd.rfdma));
|
||
|
emac_reg_w32(hw, EMAC, EMAC_DESC_CTRL_14,
|
||
|
EMAC_DMA_ADDR_LO(adpt->rx_queue[1].rrd.rrdma));
|
||
|
/* fall through */
|
||
|
case 1:
|
||
|
emac_reg_w32(hw, EMAC, EMAC_DESC_CTRL_2,
|
||
|
EMAC_DMA_ADDR_LO(adpt->rx_queue[0].rfd.rfdma));
|
||
|
emac_reg_w32(hw, EMAC, EMAC_DESC_CTRL_5,
|
||
|
EMAC_DMA_ADDR_LO(adpt->rx_queue[0].rrd.rrdma));
|
||
|
break;
|
||
|
default:
|
||
|
emac_err(adpt, "Invalid number of RX queues (%d)\n",
|
||
|
adpt->num_rxques);
|
||
|
return;
|
||
|
}
|
||
|
emac_reg_w32(hw, EMAC, EMAC_DESC_CTRL_3,
|
||
|
adpt->rx_queue[0].rfd.count & RFD_RING_SIZE_BMSK);
|
||
|
emac_reg_w32(hw, EMAC, EMAC_DESC_CTRL_6,
|
||
|
adpt->rx_queue[0].rrd.count & RRD_RING_SIZE_BMSK);
|
||
|
emac_reg_w32(hw, EMAC, EMAC_DESC_CTRL_4,
|
||
|
adpt->rxbuf_size & RX_BUFFER_SIZE_BMSK);
|
||
|
|
||
|
emac_reg_w32(hw, EMAC, EMAC_DESC_CTRL_11, 0);
|
||
|
|
||
|
wmb(); /* ensure all parameters are written before we enable them */
|
||
|
/* Load all of base address above */
|
||
|
emac_reg_w32(hw, EMAC, EMAC_INTER_SRAM_PART9, 1);
|
||
|
wmb(); /* ensure triggering HW to read ring pointers is flushed */
|
||
|
}
|
||
|
|
||
|
/* Config transmit parameters */
|
||
|
static void emac_hw_config_tx_ctrl(struct emac_hw *hw)
|
||
|
{
|
||
|
u16 tx_offload_thresh = EMAC_MAX_TX_OFFLOAD_THRESH;
|
||
|
u32 val;
|
||
|
|
||
|
emac_reg_w32(hw, EMAC, EMAC_TXQ_CTRL_1,
|
||
|
(tx_offload_thresh >> 3) &
|
||
|
JUMBO_TASK_OFFLOAD_THRESHOLD_BMSK);
|
||
|
|
||
|
val = (hw->tpd_burst << NUM_TPD_BURST_PREF_SHFT) &
|
||
|
NUM_TPD_BURST_PREF_BMSK;
|
||
|
|
||
|
val |= (TXQ_MODE | LS_8023_SP);
|
||
|
val |= (0x0100 << NUM_TXF_BURST_PREF_SHFT) &
|
||
|
NUM_TXF_BURST_PREF_BMSK;
|
||
|
|
||
|
emac_reg_w32(hw, EMAC, EMAC_TXQ_CTRL_0, val);
|
||
|
emac_reg_update32(hw, EMAC, EMAC_TXQ_CTRL_2,
|
||
|
(TXF_HWM_BMSK | TXF_LWM_BMSK), 0);
|
||
|
wmb(); /* ensure that Tx control settings are flushed to HW */
|
||
|
}
|
||
|
|
||
|
/* Config receive parameters */
|
||
|
static void emac_hw_config_rx_ctrl(struct emac_hw *hw)
|
||
|
{
|
||
|
u32 val;
|
||
|
|
||
|
val = ((hw->rfd_burst << NUM_RFD_BURST_PREF_SHFT) &
|
||
|
NUM_RFD_BURST_PREF_BMSK);
|
||
|
val |= (SP_IPV6 | CUT_THRU_EN);
|
||
|
|
||
|
emac_reg_w32(hw, EMAC, EMAC_RXQ_CTRL_0, val);
|
||
|
|
||
|
val = emac_reg_r32(hw, EMAC, EMAC_RXQ_CTRL_1);
|
||
|
val &= ~(JUMBO_1KAH_BMSK | RFD_PREF_LOW_THRESHOLD_BMSK |
|
||
|
RFD_PREF_UP_THRESHOLD_BMSK);
|
||
|
val |= (JUMBO_1KAH << JUMBO_1KAH_SHFT) |
|
||
|
(RFD_PREF_LOW_TH << RFD_PREF_LOW_THRESHOLD_SHFT) |
|
||
|
(RFD_PREF_UP_TH << RFD_PREF_UP_THRESHOLD_SHFT);
|
||
|
emac_reg_w32(hw, EMAC, EMAC_RXQ_CTRL_1, val);
|
||
|
|
||
|
val = emac_reg_r32(hw, EMAC, EMAC_RXQ_CTRL_2);
|
||
|
val &= ~(RXF_DOF_THRESHOLD_BMSK | RXF_UOF_THRESHOLD_BMSK);
|
||
|
val |= (RXF_DOF_TH << RXF_DOF_THRESHOLD_SHFT) |
|
||
|
(RXF_UOF_TH << RXF_UOF_THRESHOLD_SHFT);
|
||
|
emac_reg_w32(hw, EMAC, EMAC_RXQ_CTRL_2, val);
|
||
|
|
||
|
val = emac_reg_r32(hw, EMAC, EMAC_RXQ_CTRL_3);
|
||
|
val &= ~(RXD_TIMER_BMSK | RXD_THRESHOLD_BMSK);
|
||
|
val |= RXD_TH << RXD_THRESHOLD_SHFT;
|
||
|
emac_reg_w32(hw, EMAC, EMAC_RXQ_CTRL_3, val);
|
||
|
wmb(); /* ensure that Rx control settings are flushed to HW */
|
||
|
}
|
||
|
|
||
|
/* Config dma */
|
||
|
static void emac_hw_config_dma_ctrl(struct emac_hw *hw)
|
||
|
{
|
||
|
u32 dma_ctrl;
|
||
|
|
||
|
dma_ctrl = DMAR_REQ_PRI;
|
||
|
|
||
|
switch (hw->dma_order) {
|
||
|
case emac_dma_ord_in:
|
||
|
dma_ctrl |= IN_ORDER_MODE;
|
||
|
break;
|
||
|
case emac_dma_ord_enh:
|
||
|
dma_ctrl |= ENH_ORDER_MODE;
|
||
|
break;
|
||
|
case emac_dma_ord_out:
|
||
|
dma_ctrl |= OUT_ORDER_MODE;
|
||
|
break;
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
dma_ctrl |= (((u32)hw->dmar_block) << REGRDBLEN_SHFT) &
|
||
|
REGRDBLEN_BMSK;
|
||
|
dma_ctrl |= (((u32)hw->dmaw_block) << REGWRBLEN_SHFT) &
|
||
|
REGWRBLEN_BMSK;
|
||
|
dma_ctrl |= (((u32)hw->dmar_dly_cnt) << DMAR_DLY_CNT_SHFT) &
|
||
|
DMAR_DLY_CNT_BMSK;
|
||
|
dma_ctrl |= (((u32)hw->dmaw_dly_cnt) << DMAW_DLY_CNT_SHFT) &
|
||
|
DMAW_DLY_CNT_BMSK;
|
||
|
|
||
|
emac_reg_w32(hw, EMAC, EMAC_DMA_CTRL, dma_ctrl);
|
||
|
wmb(); /* ensure that the DMA configuration is flushed to HW */
|
||
|
}
|
||
|
|
||
|
/* Configure MAC */
|
||
|
void emac_hw_config_mac(struct emac_hw *hw)
|
||
|
{
|
||
|
u32 val;
|
||
|
|
||
|
emac_hw_set_mac_addr(hw, hw->mac_addr);
|
||
|
|
||
|
emac_hw_config_ring_ctrl(hw);
|
||
|
|
||
|
emac_reg_w32(hw, EMAC, EMAC_MAX_FRAM_LEN_CTRL,
|
||
|
hw->mtu + ETH_HLEN + VLAN_HLEN + ETH_FCS_LEN);
|
||
|
|
||
|
emac_hw_config_tx_ctrl(hw);
|
||
|
emac_hw_config_rx_ctrl(hw);
|
||
|
emac_hw_config_dma_ctrl(hw);
|
||
|
|
||
|
if (TEST_FLAG(hw, HW_PTP_CAP))
|
||
|
emac_ptp_config(hw);
|
||
|
|
||
|
val = emac_reg_r32(hw, EMAC, EMAC_AXI_MAST_CTRL);
|
||
|
val &= ~(DATA_BYTE_SWAP | MAX_BOUND);
|
||
|
val |= MAX_BTYPE;
|
||
|
emac_reg_w32(hw, EMAC, EMAC_AXI_MAST_CTRL, val);
|
||
|
|
||
|
emac_reg_w32(hw, EMAC, EMAC_CLK_GATE_CTRL, 0);
|
||
|
emac_reg_w32(hw, EMAC, EMAC_MISC_CTRL, RX_UNCPL_INT_EN);
|
||
|
wmb(); /* ensure that the MAC configuration is flushed to HW */
|
||
|
}
|
||
|
|
||
|
/* Reset MAC */
|
||
|
void emac_hw_reset_mac(struct emac_hw *hw)
|
||
|
{
|
||
|
emac_reg_w32(hw, EMAC, EMAC_INT_MASK, 0);
|
||
|
emac_reg_w32(hw, EMAC, EMAC_INT_STATUS, DIS_INT);
|
||
|
|
||
|
emac_hw_stop_mac(hw);
|
||
|
|
||
|
emac_reg_update32(hw, EMAC, EMAC_DMA_MAS_CTRL, 0, SOFT_RST);
|
||
|
wmb(); /* ensure mac is fully reset */
|
||
|
usleep_range(100, 150);
|
||
|
|
||
|
emac_reg_update32(hw, EMAC, EMAC_DMA_MAS_CTRL, 0, INT_RD_CLR_EN);
|
||
|
wmb(); /* ensure the interrupt clear-on-read setting is flushed to HW */
|
||
|
}
|
||
|
|
||
|
/* Start MAC */
|
||
|
void emac_hw_start_mac(struct emac_hw *hw)
|
||
|
{
|
||
|
struct emac_adapter *adpt = emac_hw_get_adap(hw);
|
||
|
struct emac_phy *phy = &adpt->phy;
|
||
|
u32 mac, csr1;
|
||
|
|
||
|
/* enable tx queue */
|
||
|
if (adpt->num_txques && (adpt->num_txques <= EMAC_MAX_TX_QUEUES))
|
||
|
emac_reg_update32(hw, EMAC, EMAC_TXQ_CTRL_0, 0, TXQ_EN);
|
||
|
|
||
|
/* enable rx queue */
|
||
|
if (adpt->num_rxques && (adpt->num_rxques <= EMAC_MAX_RX_QUEUES))
|
||
|
emac_reg_update32(hw, EMAC, EMAC_RXQ_CTRL_0, 0, RXQ_EN);
|
||
|
|
||
|
/* enable mac control */
|
||
|
mac = emac_reg_r32(hw, EMAC, EMAC_MAC_CTRL);
|
||
|
csr1 = emac_reg_r32(hw, EMAC_CSR, EMAC_EMAC_WRAPPER_CSR1);
|
||
|
|
||
|
mac |= TXEN | RXEN; /* enable RX/TX */
|
||
|
|
||
|
/* enable RX/TX Flow Control */
|
||
|
switch (phy->cur_fc_mode) {
|
||
|
case EMAC_FC_FULL:
|
||
|
mac |= (TXFC | RXFC);
|
||
|
break;
|
||
|
case EMAC_FC_RX_PAUSE:
|
||
|
mac |= RXFC;
|
||
|
break;
|
||
|
case EMAC_FC_TX_PAUSE:
|
||
|
mac |= TXFC;
|
||
|
break;
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
/* setup link speed */
|
||
|
mac &= ~SPEED_BMSK;
|
||
|
switch (phy->link_speed) {
|
||
|
case EMAC_LINK_SPEED_1GB_FULL:
|
||
|
mac |= ((emac_mac_speed_1000 << SPEED_SHFT) & SPEED_BMSK);
|
||
|
csr1 |= FREQ_MODE;
|
||
|
break;
|
||
|
default:
|
||
|
mac |= ((emac_mac_speed_10_100 << SPEED_SHFT) & SPEED_BMSK);
|
||
|
csr1 &= ~FREQ_MODE;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
switch (phy->link_speed) {
|
||
|
case EMAC_LINK_SPEED_1GB_FULL:
|
||
|
case EMAC_LINK_SPEED_100_FULL:
|
||
|
case EMAC_LINK_SPEED_10_FULL:
|
||
|
mac |= FULLD;
|
||
|
break;
|
||
|
default:
|
||
|
mac &= ~FULLD;
|
||
|
}
|
||
|
|
||
|
/* other parameters */
|
||
|
mac |= (CRCE | PCRCE);
|
||
|
mac |= ((hw->preamble << PRLEN_SHFT) & PRLEN_BMSK);
|
||
|
mac |= BROAD_EN;
|
||
|
mac |= FLCHK;
|
||
|
mac &= ~RX_CHKSUM_EN;
|
||
|
mac &= ~(HUGEN | VLAN_STRIP | TPAUSE | SIMR | HUGE | MULTI_ALL |
|
||
|
DEBUG_MODE | SINGLE_PAUSE_MODE);
|
||
|
|
||
|
emac_reg_w32(hw, EMAC_CSR, EMAC_EMAC_WRAPPER_CSR1, csr1);
|
||
|
emac_reg_w32(hw, EMAC, EMAC_MAC_CTRL, mac);
|
||
|
|
||
|
/* enable interrupt read clear, low power sleep mode and
|
||
|
* the irq moderators
|
||
|
*/
|
||
|
emac_reg_w32(hw, EMAC, EMAC_IRQ_MOD_TIM_INIT, hw->irq_mod);
|
||
|
emac_reg_w32(hw, EMAC, EMAC_DMA_MAS_CTRL,
|
||
|
(INT_RD_CLR_EN | LPW_MODE |
|
||
|
IRQ_MODERATOR_EN | IRQ_MODERATOR2_EN));
|
||
|
|
||
|
if (TEST_FLAG(hw, HW_PTP_CAP))
|
||
|
emac_ptp_set_linkspeed(hw, phy->link_speed);
|
||
|
|
||
|
emac_hw_config_mac_ctrl(hw);
|
||
|
|
||
|
emac_reg_update32(hw, EMAC, EMAC_ATHR_HEADER_CTRL,
|
||
|
(HEADER_ENABLE | HEADER_CNT_EN), 0);
|
||
|
|
||
|
emac_reg_update32(hw, EMAC_CSR, EMAC_EMAC_WRAPPER_CSR2, 0, WOL_EN);
|
||
|
wmb(); /* ensure that MAC setting are flushed to HW */
|
||
|
}
|
||
|
|
||
|
/* Stop MAC */
|
||
|
void emac_hw_stop_mac(struct emac_hw *hw)
|
||
|
{
|
||
|
emac_reg_update32(hw, EMAC, EMAC_RXQ_CTRL_0, RXQ_EN, 0);
|
||
|
emac_reg_update32(hw, EMAC, EMAC_TXQ_CTRL_0, TXQ_EN, 0);
|
||
|
emac_reg_update32(hw, EMAC, EMAC_MAC_CTRL, (TXEN | RXEN), 0);
|
||
|
wmb(); /* ensure mac is stopped before we proceed */
|
||
|
usleep_range(1000, 1050);
|
||
|
}
|
||
|
|
||
|
/* set MAC address */
|
||
|
void emac_hw_set_mac_addr(struct emac_hw *hw, u8 *addr)
|
||
|
{
|
||
|
u32 sta;
|
||
|
|
||
|
/* for example: 00-A0-C6-11-22-33
|
||
|
* 0<-->C6112233, 1<-->00A0.
|
||
|
*/
|
||
|
|
||
|
/* low dword */
|
||
|
sta = (((u32)addr[2]) << 24) | (((u32)addr[3]) << 16) |
|
||
|
(((u32)addr[4]) << 8) | (((u32)addr[5]));
|
||
|
emac_reg_w32(hw, EMAC, EMAC_MAC_STA_ADDR0, sta);
|
||
|
|
||
|
/* hight dword */
|
||
|
sta = (((u32)addr[0]) << 8) | (((u32)addr[1]));
|
||
|
emac_reg_w32(hw, EMAC, EMAC_MAC_STA_ADDR1, sta);
|
||
|
wmb(); /* ensure that the MAC address is flushed to HW */
|
||
|
}
|
||
|
|
||
|
/* Read one entry from the HW tx timestamp FIFO */
|
||
|
bool emac_hw_read_tx_tstamp(struct emac_hw *hw, struct emac_hwtxtstamp *ts)
|
||
|
{
|
||
|
u32 ts_idx;
|
||
|
|
||
|
ts_idx = emac_reg_r32(hw, EMAC_CSR, EMAC_EMAC_WRAPPER_TX_TS_INX);
|
||
|
|
||
|
if (ts_idx & EMAC_WRAPPER_TX_TS_EMPTY)
|
||
|
return false;
|
||
|
|
||
|
ts->ns = emac_reg_r32(hw, EMAC_CSR, EMAC_EMAC_WRAPPER_TX_TS_LO);
|
||
|
ts->sec = emac_reg_r32(hw, EMAC_CSR, EMAC_EMAC_WRAPPER_TX_TS_HI);
|
||
|
ts->ts_idx = ts_idx & EMAC_WRAPPER_TX_TS_INX_BMSK;
|
||
|
|
||
|
return true;
|
||
|
}
|