Commit 33b31426 authored by David S. Miller's avatar David S. Miller

Merge branch 'atl1c-support-for-Mikrotik-10-25G-NIC-features'

Gatis Peisenieks says:

====================
atl1c: support for Mikrotik 10/25G NIC features

The new Mikrotik 10/25G NIC maintains compatibility with existing atl1c
driver. However it does have new features.

This patch set adds support for reporting cards higher link speed, max-mtu,
enables rx csum offload and improves tx performance.

v2:
    - fixed xmit_more handling as pointed out by Eric Dumazet
    - added a more reliable link detection on Mikrotik 10/25G NIC
      since MDIO op emulation can occasionally fail
Guangbin Huang says:
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 0d59c95e ea0fbd05
...@@ -241,6 +241,8 @@ struct atl1c_tpd_ext_desc { ...@@ -241,6 +241,8 @@ struct atl1c_tpd_ext_desc {
#define RRS_PACKET_PROT_IS_IPV6_ONLY(word) \ #define RRS_PACKET_PROT_IS_IPV6_ONLY(word) \
((((word) >> RRS_PROT_ID_SHIFT) & RRS_PROT_ID_MASK) == 6) ((((word) >> RRS_PROT_ID_SHIFT) & RRS_PROT_ID_MASK) == 6)
#define RRS_MT_PROT_ID_TCPUDP BIT(19)
struct atl1c_recv_ret_status { struct atl1c_recv_ret_status {
__le32 word0; __le32 word0;
__le32 rss_hash; __le32 rss_hash;
...@@ -289,6 +291,7 @@ enum atl1c_nic_type { ...@@ -289,6 +291,7 @@ enum atl1c_nic_type {
athr_l2c_b2, athr_l2c_b2,
athr_l1d, athr_l1d,
athr_l1d_2, athr_l1d_2,
athr_mt,
}; };
enum atl1c_trans_queue { enum atl1c_trans_queue {
......
...@@ -636,6 +636,23 @@ int atl1c_phy_init(struct atl1c_hw *hw) ...@@ -636,6 +636,23 @@ int atl1c_phy_init(struct atl1c_hw *hw)
return 0; return 0;
} }
bool atl1c_get_link_status(struct atl1c_hw *hw)
{
u16 phy_data;
if (hw->nic_type == athr_mt) {
u32 spd;
AT_READ_REG(hw, REG_MT_SPEED, &spd);
return !!spd;
}
/* MII_BMSR must be read twice */
atl1c_read_phy_reg(hw, MII_BMSR, &phy_data);
atl1c_read_phy_reg(hw, MII_BMSR, &phy_data);
return !!(phy_data & BMSR_LSTATUS);
}
/* /*
* Detects the current speed and duplex settings of the hardware. * Detects the current speed and duplex settings of the hardware.
* *
...@@ -648,6 +665,15 @@ int atl1c_get_speed_and_duplex(struct atl1c_hw *hw, u16 *speed, u16 *duplex) ...@@ -648,6 +665,15 @@ int atl1c_get_speed_and_duplex(struct atl1c_hw *hw, u16 *speed, u16 *duplex)
int err; int err;
u16 phy_data; u16 phy_data;
if (hw->nic_type == athr_mt) {
u32 spd;
AT_READ_REG(hw, REG_MT_SPEED, &spd);
*speed = spd;
*duplex = FULL_DUPLEX;
return 0;
}
/* Read PHY Specific Status Register (17) */ /* Read PHY Specific Status Register (17) */
err = atl1c_read_phy_reg(hw, MII_GIGA_PSSR, &phy_data); err = atl1c_read_phy_reg(hw, MII_GIGA_PSSR, &phy_data);
if (err) if (err)
...@@ -686,15 +712,12 @@ int atl1c_phy_to_ps_link(struct atl1c_hw *hw) ...@@ -686,15 +712,12 @@ int atl1c_phy_to_ps_link(struct atl1c_hw *hw)
int ret = 0; int ret = 0;
u16 autoneg_advertised = ADVERTISED_10baseT_Half; u16 autoneg_advertised = ADVERTISED_10baseT_Half;
u16 save_autoneg_advertised; u16 save_autoneg_advertised;
u16 phy_data;
u16 mii_lpa_data; u16 mii_lpa_data;
u16 speed = SPEED_0; u16 speed = SPEED_0;
u16 duplex = FULL_DUPLEX; u16 duplex = FULL_DUPLEX;
int i; int i;
atl1c_read_phy_reg(hw, MII_BMSR, &phy_data); if (atl1c_get_link_status(hw)) {
atl1c_read_phy_reg(hw, MII_BMSR, &phy_data);
if (phy_data & BMSR_LSTATUS) {
atl1c_read_phy_reg(hw, MII_LPA, &mii_lpa_data); atl1c_read_phy_reg(hw, MII_LPA, &mii_lpa_data);
if (mii_lpa_data & LPA_10FULL) if (mii_lpa_data & LPA_10FULL)
autoneg_advertised = ADVERTISED_10baseT_Full; autoneg_advertised = ADVERTISED_10baseT_Full;
...@@ -717,9 +740,7 @@ int atl1c_phy_to_ps_link(struct atl1c_hw *hw) ...@@ -717,9 +740,7 @@ int atl1c_phy_to_ps_link(struct atl1c_hw *hw)
if (mii_lpa_data) { if (mii_lpa_data) {
for (i = 0; i < AT_SUSPEND_LINK_TIMEOUT; i++) { for (i = 0; i < AT_SUSPEND_LINK_TIMEOUT; i++) {
mdelay(100); mdelay(100);
atl1c_read_phy_reg(hw, MII_BMSR, &phy_data); if (atl1c_get_link_status(hw)) {
atl1c_read_phy_reg(hw, MII_BMSR, &phy_data);
if (phy_data & BMSR_LSTATUS) {
if (atl1c_get_speed_and_duplex(hw, &speed, if (atl1c_get_speed_and_duplex(hw, &speed,
&duplex) != 0) &duplex) != 0)
dev_dbg(&pdev->dev, dev_dbg(&pdev->dev,
......
...@@ -26,6 +26,7 @@ void atl1c_phy_disable(struct atl1c_hw *hw); ...@@ -26,6 +26,7 @@ void atl1c_phy_disable(struct atl1c_hw *hw);
void atl1c_hw_set_mac_addr(struct atl1c_hw *hw, u8 *mac_addr); void atl1c_hw_set_mac_addr(struct atl1c_hw *hw, u8 *mac_addr);
int atl1c_phy_reset(struct atl1c_hw *hw); int atl1c_phy_reset(struct atl1c_hw *hw);
int atl1c_read_mac_addr(struct atl1c_hw *hw); int atl1c_read_mac_addr(struct atl1c_hw *hw);
bool atl1c_get_link_status(struct atl1c_hw *hw);
int atl1c_get_speed_and_duplex(struct atl1c_hw *hw, u16 *speed, u16 *duplex); int atl1c_get_speed_and_duplex(struct atl1c_hw *hw, u16 *speed, u16 *duplex);
u32 atl1c_hash_mc_addr(struct atl1c_hw *hw, u8 *mc_addr); u32 atl1c_hash_mc_addr(struct atl1c_hw *hw, u8 *mc_addr);
void atl1c_hash_set(struct atl1c_hw *hw, u32 hash_value); void atl1c_hash_set(struct atl1c_hw *hw, u32 hash_value);
...@@ -764,6 +765,13 @@ void atl1c_post_phy_linkchg(struct atl1c_hw *hw, u16 link_speed); ...@@ -764,6 +765,13 @@ void atl1c_post_phy_linkchg(struct atl1c_hw *hw, u16 link_speed);
#define REG_DEBUG_DATA0 0x1900 #define REG_DEBUG_DATA0 0x1900
#define REG_DEBUG_DATA1 0x1904 #define REG_DEBUG_DATA1 0x1904
#define REG_MT_MAGIC 0x1F00
#define REG_MT_MODE 0x1F04
#define REG_MT_SPEED 0x1F08
#define REG_MT_VERSION 0x1F0C
#define MT_MAGIC 0xaabb1234
#define L1D_MPW_PHYID1 0xD01C /* V7 */ #define L1D_MPW_PHYID1 0xD01C /* V7 */
#define L1D_MPW_PHYID2 0xD01D /* V1-V6 */ #define L1D_MPW_PHYID2 0xD01D /* V1-V6 */
#define L1D_MPW_PHYID3 0xD01E /* V8 */ #define L1D_MPW_PHYID3 0xD01E /* V8 */
......
...@@ -232,15 +232,14 @@ static void atl1c_check_link_status(struct atl1c_adapter *adapter) ...@@ -232,15 +232,14 @@ static void atl1c_check_link_status(struct atl1c_adapter *adapter)
struct pci_dev *pdev = adapter->pdev; struct pci_dev *pdev = adapter->pdev;
int err; int err;
unsigned long flags; unsigned long flags;
u16 speed, duplex, phy_data; u16 speed, duplex;
bool link;
spin_lock_irqsave(&adapter->mdio_lock, flags); spin_lock_irqsave(&adapter->mdio_lock, flags);
/* MII_BMSR must read twise */ link = atl1c_get_link_status(hw);
atl1c_read_phy_reg(hw, MII_BMSR, &phy_data);
atl1c_read_phy_reg(hw, MII_BMSR, &phy_data);
spin_unlock_irqrestore(&adapter->mdio_lock, flags); spin_unlock_irqrestore(&adapter->mdio_lock, flags);
if ((phy_data & BMSR_LSTATUS) == 0) { if (!link) {
/* link down */ /* link down */
netif_carrier_off(netdev); netif_carrier_off(netdev);
hw->hibernate = true; hw->hibernate = true;
...@@ -284,16 +283,13 @@ static void atl1c_link_chg_event(struct atl1c_adapter *adapter) ...@@ -284,16 +283,13 @@ static void atl1c_link_chg_event(struct atl1c_adapter *adapter)
{ {
struct net_device *netdev = adapter->netdev; struct net_device *netdev = adapter->netdev;
struct pci_dev *pdev = adapter->pdev; struct pci_dev *pdev = adapter->pdev;
u16 phy_data; bool link;
u16 link_up;
spin_lock(&adapter->mdio_lock); spin_lock(&adapter->mdio_lock);
atl1c_read_phy_reg(&adapter->hw, MII_BMSR, &phy_data); link = atl1c_get_link_status(&adapter->hw);
atl1c_read_phy_reg(&adapter->hw, MII_BMSR, &phy_data);
spin_unlock(&adapter->mdio_lock); spin_unlock(&adapter->mdio_lock);
link_up = phy_data & BMSR_LSTATUS;
/* notify upper layer link down ASAP */ /* notify upper layer link down ASAP */
if (!link_up) { if (!link) {
if (netif_carrier_ok(netdev)) { if (netif_carrier_ok(netdev)) {
/* old link state: Up */ /* old link state: Up */
netif_carrier_off(netdev); netif_carrier_off(netdev);
...@@ -478,6 +474,9 @@ static void atl1c_set_rxbufsize(struct atl1c_adapter *adapter, ...@@ -478,6 +474,9 @@ static void atl1c_set_rxbufsize(struct atl1c_adapter *adapter,
static netdev_features_t atl1c_fix_features(struct net_device *netdev, static netdev_features_t atl1c_fix_features(struct net_device *netdev,
netdev_features_t features) netdev_features_t features)
{ {
struct atl1c_adapter *adapter = netdev_priv(netdev);
struct atl1c_hw *hw = &adapter->hw;
/* /*
* Since there is no support for separate rx/tx vlan accel * Since there is no support for separate rx/tx vlan accel
* enable/disable make sure tx flag is always in same state as rx. * enable/disable make sure tx flag is always in same state as rx.
...@@ -487,8 +486,10 @@ static netdev_features_t atl1c_fix_features(struct net_device *netdev, ...@@ -487,8 +486,10 @@ static netdev_features_t atl1c_fix_features(struct net_device *netdev,
else else
features &= ~NETIF_F_HW_VLAN_CTAG_TX; features &= ~NETIF_F_HW_VLAN_CTAG_TX;
if (netdev->mtu > MAX_TSO_FRAME_SIZE) if (hw->nic_type != athr_mt) {
features &= ~(NETIF_F_TSO | NETIF_F_TSO6); if (netdev->mtu > MAX_TSO_FRAME_SIZE)
features &= ~(NETIF_F_TSO | NETIF_F_TSO6);
}
return features; return features;
} }
...@@ -515,9 +516,12 @@ static void atl1c_set_max_mtu(struct net_device *netdev) ...@@ -515,9 +516,12 @@ static void atl1c_set_max_mtu(struct net_device *netdev)
case athr_l1d: case athr_l1d:
case athr_l1d_2: case athr_l1d_2:
netdev->max_mtu = MAX_JUMBO_FRAME_SIZE - netdev->max_mtu = MAX_JUMBO_FRAME_SIZE -
(ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN); (ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN);
break; break;
/* The 10/100 devices don't support jumbo packets, max_mtu 1500 */ case athr_mt:
netdev->max_mtu = 9500;
break;
/* The 10/100 devices don't support jumbo packets, max_mtu 1500 */
default: default:
netdev->max_mtu = ETH_DATA_LEN; netdev->max_mtu = ETH_DATA_LEN;
break; break;
...@@ -644,6 +648,7 @@ static int atl1c_alloc_queues(struct atl1c_adapter *adapter) ...@@ -644,6 +648,7 @@ static int atl1c_alloc_queues(struct atl1c_adapter *adapter)
static void atl1c_set_mac_type(struct atl1c_hw *hw) static void atl1c_set_mac_type(struct atl1c_hw *hw)
{ {
u32 magic;
switch (hw->device_id) { switch (hw->device_id) {
case PCI_DEVICE_ID_ATTANSIC_L2C: case PCI_DEVICE_ID_ATTANSIC_L2C:
hw->nic_type = athr_l2c; hw->nic_type = athr_l2c;
...@@ -662,6 +667,9 @@ static void atl1c_set_mac_type(struct atl1c_hw *hw) ...@@ -662,6 +667,9 @@ static void atl1c_set_mac_type(struct atl1c_hw *hw)
break; break;
case PCI_DEVICE_ID_ATHEROS_L1D_2_0: case PCI_DEVICE_ID_ATHEROS_L1D_2_0:
hw->nic_type = athr_l1d_2; hw->nic_type = athr_l1d_2;
AT_READ_REG(hw, REG_MT_MAGIC, &magic);
if (magic == MT_MAGIC)
hw->nic_type = athr_mt;
break; break;
default: default:
break; break;
...@@ -1659,6 +1667,11 @@ static irqreturn_t atl1c_intr(int irq, void *data) ...@@ -1659,6 +1667,11 @@ static irqreturn_t atl1c_intr(int irq, void *data)
static inline void atl1c_rx_checksum(struct atl1c_adapter *adapter, static inline void atl1c_rx_checksum(struct atl1c_adapter *adapter,
struct sk_buff *skb, struct atl1c_recv_ret_status *prrs) struct sk_buff *skb, struct atl1c_recv_ret_status *prrs)
{ {
if (adapter->hw.nic_type == athr_mt) {
if (prrs->word3 & RRS_MT_PROT_ID_TCPUDP)
skb->ip_summed = CHECKSUM_UNNECESSARY;
return;
}
/* /*
* The pid field in RRS in not correct sometimes, so we * The pid field in RRS in not correct sometimes, so we
* cannot figure out if the packet is fragmented or not, * cannot figure out if the packet is fragmented or not,
...@@ -2207,8 +2220,8 @@ static int atl1c_tx_map(struct atl1c_adapter *adapter, ...@@ -2207,8 +2220,8 @@ static int atl1c_tx_map(struct atl1c_adapter *adapter,
return -1; return -1;
} }
static void atl1c_tx_queue(struct atl1c_adapter *adapter, struct sk_buff *skb, static void atl1c_tx_queue(struct atl1c_adapter *adapter,
struct atl1c_tpd_desc *tpd, enum atl1c_trans_queue type) enum atl1c_trans_queue type)
{ {
struct atl1c_tpd_ring *tpd_ring = &adapter->tpd_ring[type]; struct atl1c_tpd_ring *tpd_ring = &adapter->tpd_ring[type];
u16 reg; u16 reg;
...@@ -2234,6 +2247,7 @@ static netdev_tx_t atl1c_xmit_frame(struct sk_buff *skb, ...@@ -2234,6 +2247,7 @@ static netdev_tx_t atl1c_xmit_frame(struct sk_buff *skb,
if (atl1c_tpd_avail(adapter, type) < tpd_req) { if (atl1c_tpd_avail(adapter, type) < tpd_req) {
/* no enough descriptor, just stop queue */ /* no enough descriptor, just stop queue */
atl1c_tx_queue(adapter, type);
netif_stop_queue(netdev); netif_stop_queue(netdev);
return NETDEV_TX_BUSY; return NETDEV_TX_BUSY;
} }
...@@ -2242,6 +2256,7 @@ static netdev_tx_t atl1c_xmit_frame(struct sk_buff *skb, ...@@ -2242,6 +2256,7 @@ static netdev_tx_t atl1c_xmit_frame(struct sk_buff *skb,
/* do TSO and check sum */ /* do TSO and check sum */
if (atl1c_tso_csum(adapter, skb, &tpd, type) != 0) { if (atl1c_tso_csum(adapter, skb, &tpd, type) != 0) {
atl1c_tx_queue(adapter, type);
dev_kfree_skb_any(skb); dev_kfree_skb_any(skb);
return NETDEV_TX_OK; return NETDEV_TX_OK;
} }
...@@ -2266,8 +2281,10 @@ static netdev_tx_t atl1c_xmit_frame(struct sk_buff *skb, ...@@ -2266,8 +2281,10 @@ static netdev_tx_t atl1c_xmit_frame(struct sk_buff *skb,
atl1c_tx_rollback(adapter, tpd, type); atl1c_tx_rollback(adapter, tpd, type);
dev_kfree_skb_any(skb); dev_kfree_skb_any(skb);
} else { } else {
netdev_sent_queue(adapter->netdev, skb->len); bool more = netdev_xmit_more();
atl1c_tx_queue(adapter, skb, tpd, type);
if (__netdev_sent_queue(adapter->netdev, skb->len, more))
atl1c_tx_queue(adapter, type);
} }
return NETDEV_TX_OK; return NETDEV_TX_OK;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment