diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2014-08-06 09:38:14 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2014-08-06 09:38:14 -0700 |
commit | ae045e2455429c418a418a3376301a9e5753a0a8 (patch) | |
tree | b445bdeecd3f38aa0d0a29c9585cee49e4ccb0f1 /drivers/net/ethernet/broadcom | |
parent | f4f142ed4ef835709c7e6d12eaca10d190bcebed (diff) | |
parent | d247b6ab3ce6dd43665780865ec5fa145d9ab6bd (diff) |
Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next
Pull networking updates from David Miller:
"Highlights:
1) Steady transitioning of the BPF instructure to a generic spot so
all kernel subsystems can make use of it, from Alexei Starovoitov.
2) SFC driver supports busy polling, from Alexandre Rames.
3) Take advantage of hash table in UDP multicast delivery, from David
Held.
4) Lighten locking, in particular by getting rid of the LRU lists, in
inet frag handling. From Florian Westphal.
5) Add support for various RFC6458 control messages in SCTP, from
Geir Ola Vaagland.
6) Allow to filter bridge forwarding database dumps by device, from
Jamal Hadi Salim.
7) virtio-net also now supports busy polling, from Jason Wang.
8) Some low level optimization tweaks in pktgen from Jesper Dangaard
Brouer.
9) Add support for ipv6 address generation modes, so that userland
can have some input into the process. From Jiri Pirko.
10) Consolidate common TCP connection request code in ipv4 and ipv6,
from Octavian Purdila.
11) New ARP packet logger in netfilter, from Pablo Neira Ayuso.
12) Generic resizable RCU hash table, with intial users in netlink and
nftables. From Thomas Graf.
13) Maintain a name assignment type so that userspace can see where a
network device name came from (enumerated by kernel, assigned
explicitly by userspace, etc.) From Tom Gundersen.
14) Automatic flow label generation on transmit in ipv6, from Tom
Herbert.
15) New packet timestamping facilities from Willem de Bruijn, meant to
assist in measuring latencies going into/out-of the packet
scheduler, latency from TCP data transmission to ACK, etc"
* git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next: (1536 commits)
cxgb4 : Disable recursive mailbox commands when enabling vi
net: reduce USB network driver config options.
tg3: Modify tg3_tso_bug() to handle multiple TX rings
amd-xgbe: Perform phy connect/disconnect at dev open/stop
amd-xgbe: Use dma_set_mask_and_coherent to set DMA mask
net: sun4i-emac: fix memory leak on bad packet
sctp: fix possible seqlock seadlock in sctp_packet_transmit()
Revert "net: phy: Set the driver when registering an MDIO bus device"
cxgb4vf: Turn off SGE RX/TX Callback Timers and interrupts in PCI shutdown routine
team: Simplify return path of team_newlink
bridge: Update outdated comment on promiscuous mode
net-timestamp: ACK timestamp for bytestreams
net-timestamp: TCP timestamping
net-timestamp: SCHED timestamp on entering packet scheduler
net-timestamp: add key to disambiguate concurrent datagrams
net-timestamp: move timestamp flags out of sk_flags
net-timestamp: extend SCM_TIMESTAMPING ancillary data struct
cxgb4i : Move stray CPL definitions to cxgb4 driver
tcp: reduce spurious retransmits due to transient SACK reneging
qlcnic: Initialize dcbnl_ops before register_netdev
...
Diffstat (limited to 'drivers/net/ethernet/broadcom')
25 files changed, 1475 insertions, 470 deletions
diff --git a/drivers/net/ethernet/broadcom/Kconfig b/drivers/net/ethernet/broadcom/Kconfig index 3e488094b073..7dcfb19a31c8 100644 --- a/drivers/net/ethernet/broadcom/Kconfig +++ b/drivers/net/ethernet/broadcom/Kconfig @@ -72,23 +72,23 @@ config BCMGENET Broadcom BCM7xxx Set Top Box family chipset. config BNX2 - tristate "Broadcom NetXtremeII support" + tristate "QLogic NetXtremeII support" depends on PCI select CRC32 select FW_LOADER ---help--- - This driver supports Broadcom NetXtremeII gigabit Ethernet cards. + This driver supports QLogic NetXtremeII gigabit Ethernet cards. To compile this driver as a module, choose M here: the module will be called bnx2. This is recommended. config CNIC - tristate "Broadcom CNIC support" + tristate "QLogic CNIC support" depends on PCI select BNX2 select UIO ---help--- - This driver supports offload features of Broadcom NetXtremeII + This driver supports offload features of QLogic NetXtremeII gigabit Ethernet cards. To compile this driver as a module, choose M here: the module diff --git a/drivers/net/ethernet/broadcom/bcmsysport.c b/drivers/net/ethernet/broadcom/bcmsysport.c index 5776e503e4c5..6f4e18644bd4 100644 --- a/drivers/net/ethernet/broadcom/bcmsysport.c +++ b/drivers/net/ethernet/broadcom/bcmsysport.c @@ -81,14 +81,14 @@ static inline void dma_desc_set_addr(struct bcm_sysport_priv *priv, { #ifdef CONFIG_PHYS_ADDR_T_64BIT __raw_writel(upper_32_bits(addr) & DESC_ADDR_HI_MASK, - d + DESC_ADDR_HI_STATUS_LEN); + d + DESC_ADDR_HI_STATUS_LEN); #endif __raw_writel(lower_32_bits(addr), d + DESC_ADDR_LO); } static inline void tdma_port_write_desc_addr(struct bcm_sysport_priv *priv, - struct dma_desc *desc, - unsigned int port) + struct dma_desc *desc, + unsigned int port) { /* Ports are latched, so write upper address first */ tdma_writel(priv, desc->addr_status_len, TDMA_WRITE_PORT_HI(port)); @@ -108,7 +108,7 @@ static int bcm_sysport_set_settings(struct net_device *dev, } static int bcm_sysport_get_settings(struct net_device *dev, - struct ethtool_cmd *cmd) + struct ethtool_cmd *cmd) { struct bcm_sysport_priv *priv = netdev_priv(dev); @@ -119,14 +119,14 @@ static int bcm_sysport_get_settings(struct net_device *dev, } static int bcm_sysport_set_rx_csum(struct net_device *dev, - netdev_features_t wanted) + netdev_features_t wanted) { struct bcm_sysport_priv *priv = netdev_priv(dev); u32 reg; - priv->rx_csum_en = !!(wanted & NETIF_F_RXCSUM); + priv->rx_chk_en = !!(wanted & NETIF_F_RXCSUM); reg = rxchk_readl(priv, RXCHK_CONTROL); - if (priv->rx_csum_en) + if (priv->rx_chk_en) reg |= RXCHK_EN; else reg &= ~RXCHK_EN; @@ -134,7 +134,7 @@ static int bcm_sysport_set_rx_csum(struct net_device *dev, /* If UniMAC forwards CRC, we need to skip over it to get * a valid CHK bit to be set in the per-packet status word */ - if (priv->rx_csum_en && priv->crc_fwd) + if (priv->rx_chk_en && priv->crc_fwd) reg |= RXCHK_SKIP_FCS; else reg &= ~RXCHK_SKIP_FCS; @@ -145,7 +145,7 @@ static int bcm_sysport_set_rx_csum(struct net_device *dev, } static int bcm_sysport_set_tx_csum(struct net_device *dev, - netdev_features_t wanted) + netdev_features_t wanted) { struct bcm_sysport_priv *priv = netdev_priv(dev); u32 reg; @@ -165,7 +165,7 @@ static int bcm_sysport_set_tx_csum(struct net_device *dev, } static int bcm_sysport_set_features(struct net_device *dev, - netdev_features_t features) + netdev_features_t features) { netdev_features_t changed = features ^ dev->features; netdev_features_t wanted = dev->wanted_features; @@ -261,7 +261,7 @@ static const struct bcm_sysport_stats bcm_sysport_gstrings_stats[] = { /* RXCHK misc statistics */ STAT_RXCHK("rxchk_bad_csum", mib.rxchk_bad_csum, RXCHK_BAD_CSUM_CNTR), STAT_RXCHK("rxchk_other_pkt_disc", mib.rxchk_other_pkt_disc, - RXCHK_OTHER_DISC_CNTR), + RXCHK_OTHER_DISC_CNTR), /* RBUF misc statistics */ STAT_RBUF("rbuf_ovflow_cnt", mib.rbuf_ovflow_cnt, RBUF_OVFL_DISC_CNTR), STAT_RBUF("rbuf_err_cnt", mib.rbuf_err_cnt, RBUF_ERR_PKT_CNTR), @@ -270,7 +270,7 @@ static const struct bcm_sysport_stats bcm_sysport_gstrings_stats[] = { #define BCM_SYSPORT_STATS_LEN ARRAY_SIZE(bcm_sysport_gstrings_stats) static void bcm_sysport_get_drvinfo(struct net_device *dev, - struct ethtool_drvinfo *info) + struct ethtool_drvinfo *info) { strlcpy(info->driver, KBUILD_MODNAME, sizeof(info->driver)); strlcpy(info->version, "0.1", sizeof(info->version)); @@ -303,7 +303,7 @@ static int bcm_sysport_get_sset_count(struct net_device *dev, int string_set) } static void bcm_sysport_get_strings(struct net_device *dev, - u32 stringset, u8 *data) + u32 stringset, u8 *data) { int i; @@ -311,8 +311,8 @@ static void bcm_sysport_get_strings(struct net_device *dev, case ETH_SS_STATS: for (i = 0; i < BCM_SYSPORT_STATS_LEN; i++) { memcpy(data + i * ETH_GSTRING_LEN, - bcm_sysport_gstrings_stats[i].stat_string, - ETH_GSTRING_LEN); + bcm_sysport_gstrings_stats[i].stat_string, + ETH_GSTRING_LEN); } break; default: @@ -362,7 +362,7 @@ static void bcm_sysport_update_mib_counters(struct bcm_sysport_priv *priv) } static void bcm_sysport_get_stats(struct net_device *dev, - struct ethtool_stats *stats, u64 *data) + struct ethtool_stats *stats, u64 *data) { struct bcm_sysport_priv *priv = netdev_priv(dev); int i; @@ -384,6 +384,64 @@ static void bcm_sysport_get_stats(struct net_device *dev, } } +static void bcm_sysport_get_wol(struct net_device *dev, + struct ethtool_wolinfo *wol) +{ + struct bcm_sysport_priv *priv = netdev_priv(dev); + u32 reg; + + wol->supported = WAKE_MAGIC | WAKE_MAGICSECURE; + wol->wolopts = priv->wolopts; + + if (!(priv->wolopts & WAKE_MAGICSECURE)) + return; + + /* Return the programmed SecureOn password */ + reg = umac_readl(priv, UMAC_PSW_MS); + put_unaligned_be16(reg, &wol->sopass[0]); + reg = umac_readl(priv, UMAC_PSW_LS); + put_unaligned_be32(reg, &wol->sopass[2]); +} + +static int bcm_sysport_set_wol(struct net_device *dev, + struct ethtool_wolinfo *wol) +{ + struct bcm_sysport_priv *priv = netdev_priv(dev); + struct device *kdev = &priv->pdev->dev; + u32 supported = WAKE_MAGIC | WAKE_MAGICSECURE; + + if (!device_can_wakeup(kdev)) + return -ENOTSUPP; + + if (wol->wolopts & ~supported) + return -EINVAL; + + /* Program the SecureOn password */ + if (wol->wolopts & WAKE_MAGICSECURE) { + umac_writel(priv, get_unaligned_be16(&wol->sopass[0]), + UMAC_PSW_MS); + umac_writel(priv, get_unaligned_be32(&wol->sopass[2]), + UMAC_PSW_LS); + } + + /* Flag the device and relevant IRQ as wakeup capable */ + if (wol->wolopts) { + device_set_wakeup_enable(kdev, 1); + enable_irq_wake(priv->wol_irq); + priv->wol_irq_disabled = 0; + } else { + device_set_wakeup_enable(kdev, 0); + /* Avoid unbalanced disable_irq_wake calls */ + if (!priv->wol_irq_disabled) + disable_irq_wake(priv->wol_irq); + priv->wol_irq_disabled = 1; + } + + priv->wolopts = wol->wolopts; + + return 0; +} + static void bcm_sysport_free_cb(struct bcm_sysport_cb *cb) { dev_kfree_skb_any(cb->skb); @@ -406,7 +464,7 @@ static int bcm_sysport_rx_refill(struct bcm_sysport_priv *priv, } mapping = dma_map_single(kdev, cb->skb->data, - RX_BUF_LENGTH, DMA_FROM_DEVICE); + RX_BUF_LENGTH, DMA_FROM_DEVICE); ret = dma_mapping_error(kdev, mapping); if (ret) { bcm_sysport_free_cb(cb); @@ -470,22 +528,20 @@ static unsigned int bcm_sysport_desc_rx(struct bcm_sysport_priv *priv, to_process = p_index - priv->rx_c_index; netif_dbg(priv, rx_status, ndev, - "p_index=%d rx_c_index=%d to_process=%d\n", - p_index, priv->rx_c_index, to_process); - - while ((processed < to_process) && - (processed < budget)) { + "p_index=%d rx_c_index=%d to_process=%d\n", + p_index, priv->rx_c_index, to_process); + while ((processed < to_process) && (processed < budget)) { cb = &priv->rx_cbs[priv->rx_read_ptr]; skb = cb->skb; dma_unmap_single(kdev, dma_unmap_addr(cb, dma_addr), - RX_BUF_LENGTH, DMA_FROM_DEVICE); + RX_BUF_LENGTH, DMA_FROM_DEVICE); /* Extract the Receive Status Block prepended */ rsb = (struct bcm_rsb *)skb->data; len = (rsb->rx_status_len >> DESC_LEN_SHIFT) & DESC_LEN_MASK; status = (rsb->rx_status_len >> DESC_STATUS_SHIFT) & - DESC_STATUS_MASK; + DESC_STATUS_MASK; processed++; priv->rx_read_ptr++; @@ -493,9 +549,9 @@ static unsigned int bcm_sysport_desc_rx(struct bcm_sysport_priv *priv, priv->rx_read_ptr = 0; netif_dbg(priv, rx_status, ndev, - "p=%d, c=%d, rd_ptr=%d, len=%d, flag=0x%04x\n", - p_index, priv->rx_c_index, priv->rx_read_ptr, - len, status); + "p=%d, c=%d, rd_ptr=%d, len=%d, flag=0x%04x\n", + p_index, priv->rx_c_index, priv->rx_read_ptr, + len, status); if (unlikely(!skb)) { netif_err(priv, rx_err, ndev, "out of memory!\n"); @@ -554,9 +610,9 @@ refill: } static void bcm_sysport_tx_reclaim_one(struct bcm_sysport_priv *priv, - struct bcm_sysport_cb *cb, - unsigned int *bytes_compl, - unsigned int *pkts_compl) + struct bcm_sysport_cb *cb, + unsigned int *bytes_compl, + unsigned int *pkts_compl) { struct device *kdev = &priv->pdev->dev; struct net_device *ndev = priv->netdev; @@ -565,8 +621,8 @@ static void bcm_sysport_tx_reclaim_one(struct bcm_sysport_priv *priv, ndev->stats.tx_bytes += cb->skb->len; *bytes_compl += cb->skb->len; dma_unmap_single(kdev, dma_unmap_addr(cb, dma_addr), - dma_unmap_len(cb, dma_len), - DMA_TO_DEVICE); + dma_unmap_len(cb, dma_len), + DMA_TO_DEVICE); ndev->stats.tx_packets++; (*pkts_compl)++; bcm_sysport_free_cb(cb); @@ -574,7 +630,7 @@ static void bcm_sysport_tx_reclaim_one(struct bcm_sysport_priv *priv, } else if (dma_unmap_addr(cb, dma_addr)) { ndev->stats.tx_bytes += dma_unmap_len(cb, dma_len); dma_unmap_page(kdev, dma_unmap_addr(cb, dma_addr), - dma_unmap_len(cb, dma_len), DMA_TO_DEVICE); + dma_unmap_len(cb, dma_len), DMA_TO_DEVICE); dma_unmap_addr_set(cb, dma_addr, 0); } } @@ -608,8 +664,8 @@ static unsigned int __bcm_sysport_tx_reclaim(struct bcm_sysport_priv *priv, last_tx_cn = num_tx_cbs - last_c_index + c_index; netif_dbg(priv, tx_done, ndev, - "ring=%d c_index=%d last_tx_cn=%d last_c_index=%d\n", - ring->index, c_index, last_tx_cn, last_c_index); + "ring=%d c_index=%d last_tx_cn=%d last_c_index=%d\n", + ring->index, c_index, last_tx_cn, last_c_index); while (last_tx_cn-- > 0) { cb = ring->cbs + last_c_index; @@ -626,8 +682,8 @@ static unsigned int __bcm_sysport_tx_reclaim(struct bcm_sysport_priv *priv, netif_tx_wake_queue(txq); netif_dbg(priv, tx_done, ndev, - "ring=%d c_index=%d pkts_compl=%d, bytes_compl=%d\n", - ring->index, ring->c_index, pkts_compl, bytes_compl); + "ring=%d c_index=%d pkts_compl=%d, bytes_compl=%d\n", + ring->index, ring->c_index, pkts_compl, bytes_compl); return pkts_compl; } @@ -692,6 +748,20 @@ static int bcm_sysport_poll(struct napi_struct *napi, int budget) return work_done; } +static void bcm_sysport_resume_from_wol(struct bcm_sysport_priv *priv) +{ + u32 reg; + + /* Stop monitoring MPD interrupt */ + intrl2_0_mask_set(priv, INTRL2_0_MPD); + + /* Clear the MagicPacket detection logic */ + reg = umac_readl(priv, UMAC_MPD_CTRL); + reg &= ~MPD_EN; + umac_writel(priv, reg, UMAC_MPD_CTRL); + + netif_dbg(priv, wol, priv->netdev, "resumed from WOL\n"); +} /* RX and misc interrupt routine */ static irqreturn_t bcm_sysport_rx_isr(int irq, void *dev_id) @@ -722,6 +792,11 @@ static irqreturn_t bcm_sysport_rx_isr(int irq, void *dev_id) if (priv->irq0_stat & INTRL2_0_TX_RING_FULL) bcm_sysport_tx_reclaim_all(priv); + if (priv->irq0_stat & INTRL2_0_MPD) { + netdev_info(priv->netdev, "Wake-on-LAN interrupt!\n"); + bcm_sysport_resume_from_wol(priv); + } + return IRQ_HANDLED; } @@ -757,6 +832,15 @@ static irqreturn_t bcm_sysport_tx_isr(int irq, void *dev_id) return IRQ_HANDLED; } +static irqreturn_t bcm_sysport_wol_isr(int irq, void *dev_id) +{ + struct bcm_sysport_priv *priv = dev_id; + + pm_wakeup_event(&priv->pdev->dev, 0); + + return IRQ_HANDLED; +} + static int bcm_sysport_insert_tsb(struct sk_buff *skb, struct net_device *dev) { struct sk_buff *nskb; @@ -804,8 +888,9 @@ static int bcm_sysport_insert_tsb(struct sk_buff *skb, struct net_device *dev) csum_info |= L4_LENGTH_VALID; if (ip_proto == IPPROTO_UDP && ip_ver == ETH_P_IP) csum_info |= L4_UDP; - } else + } else { csum_info = 0; + } tsb->l4_ptr_dest_map = csum_info; } @@ -869,7 +954,7 @@ static netdev_tx_t bcm_sysport_xmit(struct sk_buff *skb, mapping = dma_map_single(kdev, skb->data, skb_len, DMA_TO_DEVICE); if (dma_mapping_error(kdev, mapping)) { netif_err(priv, tx_err, dev, "DMA map failed at %p (len=%d)\n", - skb->data, skb_len); + skb->data, skb_len); ret = NETDEV_TX_OK; goto out; } @@ -887,7 +972,7 @@ static netdev_tx_t bcm_sysport_xmit(struct sk_buff *skb, len_status = upper_32_bits(mapping) & DESC_ADDR_HI_MASK; len_status |= (skb_len << DESC_LEN_SHIFT); len_status |= (DESC_SOP | DESC_EOP | TX_STATUS_APP_CRC) << - DESC_STATUS_SHIFT; + DESC_STATUS_SHIFT; if (skb->ip_summed == CHECKSUM_PARTIAL) len_status |= (DESC_L4_CSUM << DESC_STATUS_SHIFT); @@ -912,7 +997,7 @@ static netdev_tx_t bcm_sysport_xmit(struct sk_buff *skb, netif_tx_stop_queue(txq); netif_dbg(priv, tx_queued, dev, "ring=%d desc_count=%d, curr_desc=%d\n", - ring->index, ring->desc_count, ring->curr_desc); + ring->index, ring->desc_count, ring->curr_desc); ret = NETDEV_TX_OK; out: @@ -1010,7 +1095,7 @@ static int bcm_sysport_init_tx_ring(struct bcm_sysport_priv *priv, return -ENOMEM; } - ring->cbs = kzalloc(sizeof(struct bcm_sysport_cb) * size, GFP_KERNEL); + ring->cbs = kcalloc(size, sizeof(struct bcm_sysport_cb), GFP_KERNEL); if (!ring->cbs) { netif_err(priv, hw, priv->netdev, "CB allocation failed\n"); return -ENOMEM; @@ -1050,14 +1135,14 @@ static int bcm_sysport_init_tx_ring(struct bcm_sysport_priv *priv, napi_enable(&ring->napi); netif_dbg(priv, hw, priv->netdev, - "TDMA cfg, size=%d, desc_cpu=%p\n", - ring->size, ring->desc_cpu); + "TDMA cfg, size=%d, desc_cpu=%p\n", + ring->size, ring->desc_cpu); return 0; } static void bcm_sysport_fini_tx_ring(struct bcm_sysport_priv *priv, - unsigned int index) + unsigned int index) { struct bcm_sysport_tx_ring *ring = &priv->tx_rings[index]; struct device *kdev = &priv->pdev->dev; @@ -1088,7 +1173,7 @@ static void bcm_sysport_fini_tx_ring(struct bcm_sysport_priv *priv, /* RDMA helper */ static inline int rdma_enable_set(struct bcm_sysport_priv *priv, - unsigned int enable) + unsigned int enable) { unsigned int timeout = 1000; u32 reg; @@ -1115,7 +1200,7 @@ static inline int rdma_enable_set(struct bcm_sysport_priv *priv, /* TDMA helper */ static inline int tdma_enable_set(struct bcm_sysport_priv *priv, - unsigned int enable) + unsigned int enable) { unsigned int timeout = 1000; u32 reg; @@ -1153,8 +1238,8 @@ static int bcm_sysport_init_rx_ring(struct bcm_sysport_priv *priv) priv->rx_bd_assign_index = 0; priv->rx_c_index = 0; priv->rx_read_ptr = 0; - priv->rx_cbs = kzalloc(priv->num_rx_bds * - sizeof(struct bcm_sysport_cb), GFP_KERNEL); + priv->rx_cbs = kcalloc(priv->num_rx_bds, sizeof(struct bcm_sysport_cb), + GFP_KERNEL); if (!priv->rx_cbs) { netif_err(priv, hw, priv->netdev, "CB allocation failed\n"); return -ENOMEM; @@ -1186,8 +1271,8 @@ static int bcm_sysport_init_rx_ring(struct bcm_sysport_priv *priv) rdma_writel(priv, 1, RDMA_MBDONE_INTR); netif_dbg(priv, hw, priv->netdev, - "RDMA cfg, num_rx_bds=%d, rx_bds=%p\n", - priv->num_rx_bds, priv->rx_bds); + "RDMA cfg, num_rx_bds=%d, rx_bds=%p\n", + priv->num_rx_bds, priv->rx_bds); return 0; } @@ -1207,8 +1292,8 @@ static void bcm_sysport_fini_rx_ring(struct bcm_sysport_priv *priv) cb = &priv->rx_cbs[i]; if (dma_unmap_addr(cb, dma_addr)) dma_unmap_single(&priv->pdev->dev, - dma_unmap_addr(cb, dma_addr), - RX_BUF_LENGTH, DMA_FROM_DEVICE); + dma_unmap_addr(cb, dma_addr), + RX_BUF_LENGTH, DMA_FROM_DEVICE); bcm_sysport_free_cb(cb); } @@ -1236,15 +1321,15 @@ static void bcm_sysport_set_rx_mode(struct net_device *dev) } static inline void umac_enable_set(struct bcm_sysport_priv *priv, - unsigned int enable) + u32 mask, unsigned int enable) { u32 reg; reg = umac_readl(priv, UMAC_CMD); if (enable) - reg |= CMD_RX_EN | CMD_TX_EN; + reg |= mask; else - reg &= ~(CMD_RX_EN | CMD_TX_EN); + reg &= ~mask; umac_writel(priv, reg, UMAC_CMD); /* UniMAC stops on a packet boundary, wait for a full-sized packet @@ -1268,7 +1353,7 @@ static inline void umac_reset(struct bcm_sysport_priv *priv) } static void umac_set_hw_addr(struct bcm_sysport_priv *priv, - unsigned char *addr) + unsigned char *addr) { umac_writel(priv, (addr[0] << 24) | (addr[1] << 16) | (addr[2] << 8) | addr[3], UMAC_MAC0); @@ -1284,11 +1369,35 @@ static void topctrl_flush(struct bcm_sysport_priv *priv) topctrl_writel(priv, 0, TX_FLUSH_CNTL); } +static void bcm_sysport_netif_start(struct net_device *dev) +{ + struct bcm_sysport_priv *priv = netdev_priv(dev); + + /* Enable NAPI */ + napi_enable(&priv->napi); + + phy_start(priv->phydev); + + /* Enable TX interrupts for the 32 TXQs */ + intrl2_1_mask_clear(priv, 0xffffffff); + + /* Last call before we start the real business */ + netif_tx_start_all_queues(dev); +} + +static void rbuf_init(struct bcm_sysport_priv *priv) +{ + u32 reg; + + reg = rbuf_readl(priv, RBUF_CONTROL); + reg |= RBUF_4B_ALGN | RBUF_RSB_EN; + rbuf_writel(priv, reg, RBUF_CONTROL); +} + static int bcm_sysport_open(struct net_device *dev) { struct bcm_sysport_priv *priv = netdev_priv(dev); unsigned int i; - u32 reg; int ret; /* Reset UniMAC */ @@ -1298,12 +1407,10 @@ static int bcm_sysport_open(struct net_device *dev) topctrl_flush(priv); /* Disable the UniMAC RX/TX */ - umac_enable_set(priv, 0); + umac_enable_set(priv, CMD_RX_EN | CMD_TX_EN, 0); /* Enable RBUF 2bytes alignment and Receive Status Block */ - reg = rbuf_readl(priv, RBUF_CONTROL); - reg |= RBUF_4B_ALGN | RBUF_RSB_EN; - rbuf_writel(priv, reg, RBUF_CONTROL); + rbuf_init(priv); /* Set maximum frame length */ umac_writel(priv, UMAC_MAX_MTU_SIZE, UMAC_MAX_FRAME_LEN); @@ -1351,7 +1458,7 @@ static int bcm_sysport_open(struct net_device *dev) ret = bcm_sysport_init_tx_ring(priv, i); if (ret) { netdev_err(dev, "failed to initialize TX ring %d\n", - i); + i); goto out_free_tx_ring; } } @@ -1379,19 +1486,10 @@ static int bcm_sysport_open(struct net_device *dev) if (ret) goto out_clear_rx_int; - /* Enable NAPI */ - napi_enable(&priv->napi); - /* Turn on UniMAC TX/RX */ - umac_enable_set(priv, 1); + umac_enable_set(priv, CMD_RX_EN | CMD_TX_EN, 1); - phy_start(priv->phydev); - - /* Enable TX interrupts for the 32 TXQs */ - intrl2_1_mask_clear(priv, 0xffffffff); - - /* Last call before we start the real business */ - netif_tx_start_all_queues(dev); + bcm_sysport_netif_start(dev); return 0; @@ -1410,12 +1508,9 @@ out_phy_disconnect: return ret; } -static int bcm_sysport_stop(struct net_device *dev) +static void bcm_sysport_netif_stop(struct net_device *dev) { struct bcm_sysport_priv *priv = netdev_priv(dev); - unsigned int i; - u32 reg; - int ret; /* stop all software from updating hardware */ netif_tx_stop_all_queues(dev); @@ -1427,11 +1522,18 @@ static int bcm_sysport_stop(struct net_device *dev) intrl2_0_writel(priv, 0xffffffff, INTRL2_CPU_CLEAR); intrl2_1_mask_set(priv, 0xffffffff); intrl2_1_writel(priv, 0xffffffff, INTRL2_CPU_CLEAR); +} + +static int bcm_sysport_stop(struct net_device *dev) +{ + struct bcm_sysport_priv *priv = netdev_priv(dev); + unsigned int i; + int ret; + + bcm_sysport_netif_stop(dev); /* Disable UniMAC RX */ - reg = umac_readl(priv, UMAC_CMD); - reg &= ~CMD_RX_EN; - umac_writel(priv, reg, UMAC_CMD); + umac_enable_set(priv, CMD_RX_EN, 0); ret = tdma_enable_set(priv, 0); if (ret) { @@ -1449,9 +1551,7 @@ static int bcm_sysport_stop(struct net_device *dev) } /* Disable UniMAC TX */ - reg = umac_readl(priv, UMAC_CMD); - reg &= ~CMD_TX_EN; - umac_writel(priv, reg, UMAC_CMD); + umac_enable_set(priv, CMD_TX_EN, 0); /* Free RX/TX rings SW structures */ for (i = 0; i < dev->num_tx_queues; i++) @@ -1477,6 +1577,8 @@ static struct ethtool_ops bcm_sysport_ethtool_ops = { .get_strings = bcm_sysport_get_strings, .get_ethtool_stats = bcm_sysport_get_stats, .get_sset_count = bcm_sysport_get_sset_count, + .get_wol = bcm_sysport_get_wol, + .set_wol = bcm_sysport_set_wol, }; static const struct net_device_ops bcm_sysport_netdev_ops = { @@ -1518,6 +1620,7 @@ static int bcm_sysport_probe(struct platform_device *pdev) priv->irq0 = platform_get_irq(pdev, 0); priv->irq1 = platform_get_irq(pdev, 1); + priv->wol_irq = platform_get_irq(pdev, 2); if (priv->irq0 <= 0 || priv->irq1 <= 0) { dev_err(&pdev->dev, "invalid interrupts\n"); ret = -EINVAL; @@ -1570,6 +1673,13 @@ static int bcm_sysport_probe(struct platform_device *pdev) dev->hw_features |= NETIF_F_RXCSUM | NETIF_F_HIGHDMA | NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM; + /* Request the WOL interrupt and advertise suspend if available */ + priv->wol_irq_disabled = 1; + ret = devm_request_irq(&pdev->dev, priv->wol_irq, + bcm_sysport_wol_isr, 0, dev->name, priv); + if (!ret) + device_set_wakeup_capable(&pdev->dev, 1); + /* Set the needed headroom once and for all */ BUILD_BUG_ON(sizeof(struct bcm_tsb) != 8); dev->needed_headroom += sizeof(struct bcm_tsb); @@ -1585,10 +1695,10 @@ static int bcm_sysport_probe(struct platform_device *pdev) priv->rev = topctrl_readl(priv, REV_CNTL) & REV_MASK; dev_info(&pdev->dev, - "Broadcom SYSTEMPORT" REV_FMT - " at 0x%p (irqs: %d, %d, TXQs: %d, RXQs: %d)\n", - (priv->rev >> 8) & 0xff, priv->rev & 0xff, - priv->base, priv->irq0, priv->irq1, txq, rxq); + "Broadcom SYSTEMPORT" REV_FMT + " at 0x%p (irqs: %d, %d, TXQs: %d, RXQs: %d)\n", + (priv->rev >> 8) & 0xff, priv->rev & 0xff, + priv->base, priv->irq0, priv->irq1, txq, rxq); return 0; err: @@ -1610,6 +1720,208 @@ static int bcm_sysport_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_PM_SLEEP +static int bcm_sysport_suspend_to_wol(struct bcm_sysport_priv *priv) +{ + struct net_device *ndev = priv->netdev; + unsigned int timeout = 1000; + u32 reg; + + /* Password has already been programmed */ + reg = umac_readl(priv, UMAC_MPD_CTRL); + reg |= MPD_EN; + reg &= ~PSW_EN; + if (priv->wolopts & WAKE_MAGICSECURE) + reg |= PSW_EN; + umac_writel(priv, reg, UMAC_MPD_CTRL); + + /* Make sure RBUF entered WoL mode as result */ + do { + reg = rbuf_readl(priv, RBUF_STATUS); + if (reg & RBUF_WOL_MODE) + break; + + udelay(10); + } while (timeout-- > 0); + + /* Do not leave the UniMAC RBUF matching only MPD packets */ + if (!timeout) { + reg = umac_readl(priv, UMAC_MPD_CTRL); + reg &= ~MPD_EN; + umac_writel(priv, reg, UMAC_MPD_CTRL); + netif_err(priv, wol, ndev, "failed to enter WOL mode\n"); + return -ETIMEDOUT; + } + + /* UniMAC receive needs to be turned on */ + umac_enable_set(priv, CMD_RX_EN, 1); + + /* Enable the interrupt wake-up source */ + intrl2_0_mask_clear(priv, INTRL2_0_MPD); + + netif_dbg(priv, wol, ndev, "entered WOL mode\n"); + + return 0; +} + +static int bcm_sysport_suspend(struct device *d) +{ + struct net_device *dev = dev_get_drvdata(d); + struct bcm_sysport_priv *priv = netdev_priv(dev); + unsigned int i; + int ret = 0; + u32 reg; + + if (!netif_running(dev)) + return 0; + + bcm_sysport_netif_stop(dev); + + phy_suspend(priv->phydev); + + netif_device_detach(dev); + + /* Disable UniMAC RX */ + umac_enable_set(priv, CMD_RX_EN, 0); + + ret = rdma_enable_set(priv, 0); + if (ret) { + netdev_err(dev, "RDMA timeout!\n"); + return ret; + } + + /* Disable RXCHK if enabled */ + if (priv->rx_chk_en) { + reg = rxchk_readl(priv, RXCHK_CONTROL); + reg &= ~RXCHK_EN; + rxchk_writel(priv, reg, RXCHK_CONTROL); + } + + /* Flush RX pipe */ + if (!priv->wolopts) + topctrl_writel(priv, RX_FLUSH, RX_FLUSH_CNTL); + + ret = tdma_enable_set(priv, 0); + if (ret) { + netdev_err(dev, "TDMA timeout!\n"); + return ret; + } + + /* Wait for a packet boundary */ + usleep_range(2000, 3000); + + umac_enable_set(priv, CMD_TX_EN, 0); + + topctrl_writel(priv, TX_FLUSH, TX_FLUSH_CNTL); + + /* Free RX/TX rings SW structures */ + for (i = 0; i < dev->num_tx_queues; i++) + bcm_sysport_fini_tx_ring(priv, i); + bcm_sysport_fini_rx_ring(priv); + + /* Get prepared for Wake-on-LAN */ + if (device_may_wakeup(d) && priv->wolopts) + ret = bcm_sysport_suspend_to_wol(priv); + + return ret; +} + +static int bcm_sysport_resume(struct device *d) +{ + struct net_device *dev = dev_get_drvdata(d); + struct bcm_sysport_priv *priv = netdev_priv(dev); + unsigned int i; + u32 reg; + int ret; + + if (!netif_running(dev)) + return 0; + + /* We may have been suspended and never received a WOL event that + * would turn off MPD detection, take care of that now + */ + bcm_sysport_resume_from_wol(priv); + + /* Initialize both hardware and software ring */ + for (i = 0; i < dev->num_tx_queues; i++) { + ret = bcm_sysport_init_tx_ring(priv, i); + if (ret) { + netdev_err(dev, "failed to initialize TX ring %d\n", + i); + goto out_free_tx_rings; + } + } + + /* Initialize linked-list */ + tdma_writel(priv, TDMA_LL_RAM_INIT_BUSY, TDMA_STATUS); + + /* Initialize RX ring */ + ret = bcm_sysport_init_rx_ring(priv); + if (ret) { + netdev_err(dev, "failed to initialize RX ring\n"); + goto out_free_rx_ring; + } + + netif_device_attach(dev); + + /* Enable RX interrupt and TX ring full interrupt */ + intrl2_0_mask_clear(priv, INTRL2_0_RDMA_MBDONE | INTRL2_0_TX_RING_FULL); + + /* RX pipe enable */ + topctrl_writel(priv, 0, RX_FLUSH_CNTL); + + ret = rdma_enable_set(priv, 1); + if (ret) { + netdev_err(dev, "failed to enable RDMA\n"); + goto out_free_rx_ring; + } + + /* Enable rxhck */ + if (priv->rx_chk_en) { + reg = rxchk_readl(priv, RXCHK_CONTROL); + reg |= RXCHK_EN; + rxchk_writel(priv, reg, RXCHK_CONTROL); + } + + rbuf_init(priv); + + /* Set maximum frame length */ + umac_writel(priv, UMAC_MAX_MTU_SIZE, UMAC_MAX_FRAME_LEN); + + /* Set MAC address */ + umac_set_hw_addr(priv, dev->dev_addr); + + umac_enable_set(priv, CMD_RX_EN, 1); + + /* TX pipe enable */ + topctrl_writel(priv, 0, TX_FLUSH_CNTL); + + umac_enable_set(priv, CMD_TX_EN, 1); + + ret = tdma_enable_set(priv, 1); + if (ret) { + netdev_err(dev, "TDMA timeout!\n"); + goto out_free_rx_ring; + } + + phy_resume(priv->phydev); + + bcm_sysport_netif_start(dev); + + return 0; + +out_free_rx_ring: + bcm_sysport_fini_rx_ring(priv); +out_free_tx_rings: + for (i = 0; i < dev->num_tx_queues; i++) + bcm_sysport_fini_tx_ring(priv, i); + return ret; +} +#endif + +static SIMPLE_DEV_PM_OPS(bcm_sysport_pm_ops, + bcm_sysport_suspend, bcm_sysport_resume); + static const struct of_device_id bcm_sysport_of_match[] = { { .compatible = "brcm,systemport-v1.00" }, { .compatible = "brcm,systemport" }, @@ -1623,6 +1935,7 @@ static struct platform_driver bcm_sysport_driver = { .name = "brcm-systemport", .owner = THIS_MODULE, .of_match_table = bcm_sysport_of_match, + .pm = &bcm_sysport_pm_ops, }, }; module_platform_driver(bcm_sysport_driver); diff --git a/drivers/net/ethernet/broadcom/bcmsysport.h b/drivers/net/ethernet/broadcom/bcmsysport.h index 281c08246037..b08dab828101 100644 --- a/drivers/net/ethernet/broadcom/bcmsysport.h +++ b/drivers/net/ethernet/broadcom/bcmsysport.h @@ -246,6 +246,15 @@ struct bcm_rsb { #define MIB_RX_CNT_RST (1 << 0) #define MIB_RUNT_CNT_RST (1 << 1) #define MIB_TX_CNT_RST (1 << 2) + +#define UMAC_MPD_CTRL 0x620 +#define MPD_EN (1 << 0) +#define MSEQ_LEN_SHIFT 16 +#define MSEQ_LEN_MASK 0xff +#define PSW_EN (1 << 27) + +#define UMAC_PSW_MS 0x624 +#define UMAC_PSW_LS 0x628 #define UMAC_MDF_CTRL 0x650 #define UMAC_MDF_ADDR 0x654 @@ -642,6 +651,7 @@ struct bcm_sysport_priv { struct platform_device *pdev; int irq0; int irq1; + int wol_irq; /* Transmit rings */ struct bcm_sysport_tx_ring tx_rings[TDMA_NUM_RINGS]; @@ -664,10 +674,12 @@ struct bcm_sysport_priv { int old_duplex; /* Misc fields */ - unsigned int rx_csum_en:1; + unsigned int rx_chk_en:1; unsigned int tsb_en:1; unsigned int crc_fwd:1; u16 rev; + u32 wolopts; + unsigned int wol_irq_disabled:1; /* MIB related fields */ struct bcm_sysport_mib mib; diff --git a/drivers/net/ethernet/broadcom/bnx2.c b/drivers/net/ethernet/broadcom/bnx2.c index 67d2b0047371..e64c963fe775 100644 --- a/drivers/net/ethernet/broadcom/bnx2.c +++ b/drivers/net/ethernet/broadcom/bnx2.c @@ -1,6 +1,7 @@ -/* bnx2.c: Broadcom NX2 network driver. +/* bnx2.c: QLogic NX2 network driver. * - * Copyright (c) 2004-2013 Broadcom Corporation + * Copyright (c) 2004-2014 Broadcom Corporation + * Copyright (c) 2014 QLogic Corporation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -71,10 +72,10 @@ #define TX_TIMEOUT (5*HZ) static char version[] = - "Broadcom NetXtreme II Gigabit Ethernet Driver " DRV_MODULE_NAME " v" DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")\n"; + "QLogic NetXtreme II Gigabit Ethernet Driver " DRV_MODULE_NAME " v" DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")\n"; MODULE_AUTHOR("Michael Chan <mchan@broadcom.com>"); -MODULE_DESCRIPTION("Broadcom NetXtreme II BCM5706/5708/5709/5716 Driver"); +MODULE_DESCRIPTION("QLogic NetXtreme II BCM5706/5708/5709/5716 Driver"); MODULE_LICENSE("GPL"); MODULE_VERSION(DRV_MODULE_VERSION); MODULE_FIRMWARE(FW_MIPS_FILE_06); diff --git a/drivers/net/ethernet/broadcom/bnx2.h b/drivers/net/ethernet/broadcom/bnx2.h index e341bc366fa5..28df35d35893 100644 --- a/drivers/net/ethernet/broadcom/bnx2.h +++ b/drivers/net/ethernet/broadcom/bnx2.h @@ -1,6 +1,7 @@ -/* bnx2.h: Broadcom NX2 network driver. +/* bnx2.h: QLogic NX2 network driver. * - * Copyright (c) 2004-2013 Broadcom Corporation + * Copyright (c) 2004-2014 Broadcom Corporation + * Copyright (c) 2014 QLogic Corporation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/drivers/net/ethernet/broadcom/bnx2_fw.h b/drivers/net/ethernet/broadcom/bnx2_fw.h index 940eb91f209d..7db79c28b5ff 100644 --- a/drivers/net/ethernet/broadcom/bnx2_fw.h +++ b/drivers/net/ethernet/broadcom/bnx2_fw.h @@ -1,6 +1,7 @@ -/* bnx2_fw.h: Broadcom NX2 network driver. +/* bnx2_fw.h: QLogic NX2 network driver. * * Copyright (c) 2004, 2005, 2006, 2007 Broadcom Corporation + * Copyright (c) 2014 QLogic Corporation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h index 8206a293e6b4..d777fae86988 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h @@ -1483,6 +1483,7 @@ struct bnx2x { union pf_vf_bulletin *pf2vf_bulletin; dma_addr_t pf2vf_bulletin_mapping; + union pf_vf_bulletin shadow_bulletin; struct pf_vf_bulletin_content old_bulletin; u16 requested_nr_virtfn; @@ -1508,8 +1509,10 @@ struct bnx2x { /* TCP with Timestamp Option (32) + IPv6 (40) */ #define ETH_MAX_TPA_HEADER_SIZE 72 - /* Max supported alignment is 256 (8 shift) */ -#define BNX2X_RX_ALIGN_SHIFT min(8, L1_CACHE_SHIFT) + /* Max supported alignment is 256 (8 shift) + * minimal alignment shift 6 is optimal for 57xxx HW performance + */ +#define BNX2X_RX_ALIGN_SHIFT max(6, min(8, L1_CACHE_SHIFT)) /* FW uses 2 Cache lines Alignment for start packet and size * @@ -1929,6 +1932,8 @@ struct bnx2x { struct semaphore stats_sema; u8 phys_port_id[ETH_ALEN]; + + struct bnx2x_link_report_data vf_link_vars; }; /* Tx queues may be less or equal to Rx queues */ diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c index c43e7238de21..4e6c82e20224 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c @@ -1192,29 +1192,38 @@ u16 bnx2x_get_mf_speed(struct bnx2x *bp) static void bnx2x_fill_report_data(struct bnx2x *bp, struct bnx2x_link_report_data *data) { - u16 line_speed = bnx2x_get_mf_speed(bp); - memset(data, 0, sizeof(*data)); - /* Fill the report data: effective line speed */ - data->line_speed = line_speed; - - /* Link is down */ - if (!bp->link_vars.link_up || (bp->flags & MF_FUNC_DIS)) - __set_bit(BNX2X_LINK_REPORT_LINK_DOWN, - &data->link_report_flags); - - /* Full DUPLEX */ - if (bp->link_vars.duplex == DUPLEX_FULL) - __set_bit(BNX2X_LINK_REPORT_FD, &data->link_report_flags); - - /* Rx Flow Control is ON */ - if (bp->link_vars.flow_ctrl & BNX2X_FLOW_CTRL_RX) - __set_bit(BNX2X_LINK_REPORT_RX_FC_ON, &data->link_report_flags); - - /* Tx Flow Control is ON */ - if (bp->link_vars.flow_ctrl & BNX2X_FLOW_CTRL_TX) - __set_bit(BNX2X_LINK_REPORT_TX_FC_ON, &data->link_report_flags); + if (IS_PF(bp)) { + /* Fill the report data: effective line speed */ + data->line_speed = bnx2x_get_mf_speed(bp); + + /* Link is down */ + if (!bp->link_vars.link_up || (bp->flags & MF_FUNC_DIS)) + __set_bit(BNX2X_LINK_REPORT_LINK_DOWN, + &data->link_report_flags); + + if (!BNX2X_NUM_ETH_QUEUES(bp)) + __set_bit(BNX2X_LINK_REPORT_LINK_DOWN, + &data->link_report_flags); + + /* Full DUPLEX */ + if (bp->link_vars.duplex == DUPLEX_FULL) + __set_bit(BNX2X_LINK_REPORT_FD, + &data->link_report_flags); + + /* Rx Flow Control is ON */ + if (bp->link_vars.flow_ctrl & BNX2X_FLOW_CTRL_RX) + __set_bit(BNX2X_LINK_REPORT_RX_FC_ON, + &data->link_report_flags); + + /* Tx Flow Control is ON */ + if (bp->link_vars.flow_ctrl & BNX2X_FLOW_CTRL_TX) + __set_bit(BNX2X_LINK_REPORT_TX_FC_ON, + &data->link_report_flags); + } else { /* VF */ + *data = bp->vf_link_vars; + } } /** @@ -1268,6 +1277,10 @@ void __bnx2x_link_report(struct bnx2x *bp) */ memcpy(&bp->last_reported_link, &cur_data, sizeof(cur_data)); + /* propagate status to VFs */ + if (IS_PF(bp)) + bnx2x_iov_link_update(bp); + if (test_bit(BNX2X_LINK_REPORT_LINK_DOWN, &cur_data.link_report_flags)) { netif_carrier_off(bp->dev); diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_dcb.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_dcb.c index 51a952c51cb1..fb26bc4c42a1 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_dcb.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_dcb.c @@ -2303,8 +2303,8 @@ static int bnx2x_set_admin_app_up(struct bnx2x *bp, u8 idtype, u16 idval, u8 up) return 0; } -static u8 bnx2x_dcbnl_set_app_up(struct net_device *netdev, u8 idtype, - u16 idval, u8 up) +static int bnx2x_dcbnl_set_app_up(struct net_device *netdev, u8 idtype, + u16 idval, u8 up) { struct bnx2x *bp = netdev_priv(netdev); diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c index 25eddd90f482..92fee842f954 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c @@ -216,6 +216,43 @@ static int bnx2x_get_port_type(struct bnx2x *bp) return port_type; } +static int bnx2x_get_vf_settings(struct net_device *dev, + struct ethtool_cmd *cmd) +{ + struct bnx2x *bp = netdev_priv(dev); + + if (bp->state == BNX2X_STATE_OPEN) { + if (test_bit(BNX2X_LINK_REPORT_FD, + &bp->vf_link_vars.link_report_flags)) + cmd->duplex = DUPLEX_FULL; + else + cmd->duplex = DUPLEX_HALF; + + ethtool_cmd_speed_set(cmd, bp->vf_link_vars.line_speed); + } else { + cmd->duplex = DUPLEX_UNKNOWN; + ethtool_cmd_speed_set(cmd, SPEED_UNKNOWN); + } + + cmd->port = PORT_OTHER; + cmd->phy_address = 0; + cmd->transceiver = XCVR_INTERNAL; + cmd->autoneg = AUTONEG_DISABLE; + cmd->maxtxpkt = 0; + cmd->maxrxpkt = 0; + + DP(BNX2X_MSG_ETHTOOL, "ethtool_cmd: cmd %d\n" + " supported 0x%x advertising 0x%x speed %u\n" + " duplex %d port %d phy_address %d transceiver %d\n" + " autoneg %d maxtxpkt %d maxrxpkt %d\n", + cmd->cmd, cmd->supported, cmd->advertising, + ethtool_cmd_speed(cmd), + cmd->duplex, cmd->port, cmd->phy_address, cmd->transceiver, + cmd->autoneg, cmd->maxtxpkt, cmd->maxrxpkt); + + return 0; +} + static int bnx2x_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) { struct bnx2x *bp = netdev_priv(dev); @@ -1111,6 +1148,10 @@ static u32 bnx2x_get_link(struct net_device *dev) if (bp->flags & MF_FUNC_DIS || (bp->state != BNX2X_STATE_OPEN)) return 0; + if (IS_VF(bp)) + return !test_bit(BNX2X_LINK_REPORT_LINK_DOWN, + &bp->vf_link_vars.link_report_flags); + return bp->link_vars.link_up; } @@ -3485,8 +3526,7 @@ static const struct ethtool_ops bnx2x_ethtool_ops = { }; static const struct ethtool_ops bnx2x_vf_ethtool_ops = { - .get_settings = bnx2x_get_settings, - .set_settings = bnx2x_set_settings, + .get_settings = bnx2x_get_vf_settings, .get_drvinfo = bnx2x_get_drvinfo, .get_msglevel = bnx2x_get_msglevel, .set_msglevel = bnx2x_set_msglevel, diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c index 6a8b1453a1b9..3871ec49cc4d 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c @@ -2698,6 +2698,14 @@ void bnx2x__link_status_update(struct bnx2x *bp) bp->link_vars.duplex = DUPLEX_FULL; bp->link_vars.flow_ctrl = BNX2X_FLOW_CTRL_NONE; __bnx2x_link_report(bp); + + bnx2x_sample_bulletin(bp); + + /* if bulletin board did not have an update for link status + * __bnx2x_link_report will report current status + * but it will NOT duplicate report in case of already reported + * during sampling bulletin board. + */ bnx2x_stats_handle(bp, STATS_EVENT_LINK_UP); } } @@ -12424,6 +12432,7 @@ static const struct net_device_ops bnx2x_netdev_ops = { .ndo_busy_poll = bnx2x_low_latency_recv, #endif .ndo_get_phys_port_id = bnx2x_get_phys_port_id, + .ndo_set_vf_link_state = bnx2x_set_vf_link_state, }; static int bnx2x_set_coherency_mask(struct bnx2x *bp) diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.c index eda8583f6fc0..662310c5f4e9 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.c @@ -24,6 +24,11 @@ #include <linux/crc32.h> #include <linux/if_vlan.h> +static int bnx2x_vf_op_prep(struct bnx2x *bp, int vfidx, + struct bnx2x_virtf **vf, + struct pf_vf_bulletin_content **bulletin, + bool test_queue); + /* General service functions */ static void storm_memset_vf_to_pf(struct bnx2x *bp, u16 abs_fid, u16 pf_id) @@ -597,8 +602,7 @@ int bnx2x_vf_mcast(struct bnx2x *bp, struct bnx2x_virtf *vf, rc = bnx2x_config_mcast(bp, &mcast, BNX2X_MCAST_CMD_DEL); if (rc) { BNX2X_ERR("Failed to remove multicasts\n"); - if (mc) - kfree(mc); + kfree(mc); return rc; } @@ -1328,6 +1332,8 @@ int bnx2x_iov_init_one(struct bnx2x *bp, int int_mode_param, /* Prepare the VFs event synchronization mechanism */ mutex_init(&bp->vfdb->event_mutex); + mutex_init(&bp->vfdb->bulletin_mutex); + return 0; failed: DP(BNX2X_MSG_IOV, "Failed err=%d\n", err); @@ -1473,6 +1479,107 @@ static void bnx2x_vfq_init(struct bnx2x *bp, struct bnx2x_virtf *vf, vf->abs_vfid, q->sp_obj.func_id, q->cid); } +static int bnx2x_max_speed_cap(struct bnx2x *bp) +{ + u32 supported = bp->port.supported[bnx2x_get_link_cfg_idx(bp)]; + + if (supported & + (SUPPORTED_20000baseMLD2_Full | SUPPORTED_20000baseKR2_Full)) + return 20000; + + return 10000; /* assume lowest supported speed is 10G */ +} + +int bnx2x_iov_link_update_vf(struct bnx2x *bp, int idx) +{ + struct bnx2x_link_report_data *state = &bp->last_reported_link; + struct pf_vf_bulletin_content *bulletin; + struct bnx2x_virtf *vf; + bool update = true; + int rc = 0; + + /* sanity and init */ + rc = bnx2x_vf_op_prep(bp, idx, &vf, &bulletin, false); + if (rc) + return rc; + + mutex_lock(&bp->vfdb->bulletin_mutex); + + if (vf->link_cfg == IFLA_VF_LINK_STATE_AUTO) { + bulletin->valid_bitmap |= 1 << LINK_VALID; + + bulletin->link_speed = state->line_speed; + bulletin->link_flags = 0; + if (test_bit(BNX2X_LINK_REPORT_LINK_DOWN, + &state->link_report_flags)) + bulletin->link_flags |= VFPF_LINK_REPORT_LINK_DOWN; + if (test_bit(BNX2X_LINK_REPORT_FD, + &state->link_report_flags)) + bulletin->link_flags |= VFPF_LINK_REPORT_FULL_DUPLEX; + if (test_bit(BNX2X_LINK_REPORT_RX_FC_ON, + &state->link_report_flags)) + bulletin->link_flags |= VFPF_LINK_REPORT_RX_FC_ON; + if (test_bit(BNX2X_LINK_REPORT_TX_FC_ON, + &state->link_report_flags)) + bulletin->link_flags |= VFPF_LINK_REPORT_TX_FC_ON; + } else if (vf->link_cfg == IFLA_VF_LINK_STATE_DISABLE && + !(bulletin->link_flags & VFPF_LINK_REPORT_LINK_DOWN)) { + bulletin->valid_bitmap |= 1 << LINK_VALID; + bulletin->link_flags |= VFPF_LINK_REPORT_LINK_DOWN; + } else if (vf->link_cfg == IFLA_VF_LINK_STATE_ENABLE && + (bulletin->link_flags & VFPF_LINK_REPORT_LINK_DOWN)) { + bulletin->valid_bitmap |= 1 << LINK_VALID; + bulletin->link_speed = bnx2x_max_speed_cap(bp); + bulletin->link_flags &= ~VFPF_LINK_REPORT_LINK_DOWN; + } else { + update = false; + } + + if (update) { + DP(NETIF_MSG_LINK | BNX2X_MSG_IOV, + "vf %d mode %u speed %d flags %x\n", idx, + vf->link_cfg, bulletin->link_speed, bulletin->link_flags); + + /* Post update on VF's bulletin board */ + rc = bnx2x_post_vf_bulletin(bp, idx); + if (rc) { + BNX2X_ERR("failed to update VF[%d] bulletin\n", idx); + goto out; + } + } + +out: + mutex_unlock(&bp->vfdb->bulletin_mutex); + return rc; +} + +int bnx2x_set_vf_link_state(struct net_device *dev, int idx, int link_state) +{ + struct bnx2x *bp = netdev_priv(dev); + struct bnx2x_virtf *vf = BP_VF(bp, idx); + + if (!vf) + return -EINVAL; + + if (vf->link_cfg == link_state) + return 0; /* nothing todo */ + + vf->link_cfg = link_state; + + return bnx2x_iov_link_update_vf(bp, idx); +} + +void bnx2x_iov_link_update(struct bnx2x *bp) +{ + int vfid; + + if (!IS_SRIOV(bp)) + return; + + for_each_vf(bp, vfid) + bnx2x_iov_link_update_vf(bp, vfid); +} + /* called by bnx2x_nic_load */ int bnx2x_iov_nic_init(struct bnx2x *bp) { @@ -2510,22 +2617,23 @@ void bnx2x_disable_sriov(struct bnx2x *bp) pci_disable_sriov(bp->pdev); } -static int bnx2x_vf_ndo_prep(struct bnx2x *bp, int vfidx, - struct bnx2x_virtf **vf, - struct pf_vf_bulletin_content **bulletin) +static int bnx2x_vf_op_prep(struct bnx2x *bp, int vfidx, + struct bnx2x_virtf **vf, + struct pf_vf_bulletin_content **bulletin, + bool test_queue) { if (bp->state != BNX2X_STATE_OPEN) { - BNX2X_ERR("vf ndo called though PF is down\n"); + BNX2X_ERR("PF is down - can't utilize iov-related functionality\n"); return -EINVAL; } if (!IS_SRIOV(bp)) { - BNX2X_ERR("vf ndo called though sriov is disabled\n"); + BNX2X_ERR("sriov is disabled - can't utilize iov-realted functionality\n"); return -EINVAL; } if (vfidx >= BNX2X_NR_VIRTFN(bp)) { - BNX2X_ERR("vf ndo called for uninitialized VF. vfidx was %d BNX2X_NR_VIRTFN was %d\n", + BNX2X_ERR("VF is uninitialized - can't utilize iov-related functionality. vfidx was %d BNX2X_NR_VIRTFN was %d\n", vfidx, BNX2X_NR_VIRTFN(bp)); return -EINVAL; } @@ -2535,19 +2643,18 @@ static int bnx2x_vf_ndo_prep(struct bnx2x *bp, int vfidx, *bulletin = BP_VF_BULLETIN(bp, vfidx); if (!*vf) { - BNX2X_ERR("vf ndo called but vf struct is null. vfidx was %d\n", - vfidx); + BNX2X_ERR("Unable to get VF structure for vfidx %d\n", vfidx); return -EINVAL; } - if (!(*vf)->vfqs) { - BNX2X_ERR("vf ndo called but vfqs struct is null. Was ndo invoked before dynamically enabling SR-IOV? vfidx was %d\n", + if (test_queue && !(*vf)->vfqs) { + BNX2X_ERR("vfqs struct is null. Was this invoked before dynamically enabling SR-IOV? vfidx was %d\n", vfidx); return -EINVAL; } if (!*bulletin) { - BNX2X_ERR("vf ndo called but Bulletin Board struct is null. vfidx was %d\n", + BNX2X_ERR("Bulletin Board struct is null for vfidx %d\n", vfidx); return -EINVAL; } @@ -2566,9 +2673,10 @@ int bnx2x_get_vf_config(struct net_device *dev, int vfidx, int rc; /* sanity and init */ - rc = bnx2x_vf_ndo_prep(bp, vfidx, &vf, &bulletin); + rc = bnx2x_vf_op_prep(bp, vfidx, &vf, &bulletin, true); if (rc) return rc; + mac_obj = &bnx2x_leading_vfq(vf, mac_obj); vlan_obj = &bnx2x_leading_vfq(vf, vlan_obj); if (!mac_obj || !vlan_obj) { @@ -2591,6 +2699,7 @@ int bnx2x_get_vf_config(struct net_device *dev, int vfidx, VLAN_HLEN); } } else { + mutex_lock(&bp->vfdb->bulletin_mutex); /* mac */ if (bulletin->valid_bitmap & (1 << MAC_ADDR_VALID)) /* mac configured by ndo so its in bulletin board */ @@ -2606,6 +2715,8 @@ int bnx2x_get_vf_config(struct net_device *dev, int vfidx, else /* function has not been loaded yet. Show vlans as 0s */ memset(&ivi->vlan, 0, VLAN_HLEN); + + mutex_unlock(&bp->vfdb->bulletin_mutex); } return 0; @@ -2635,15 +2746,18 @@ int bnx2x_set_vf_mac(struct net_device *dev, int vfidx, u8 *mac) struct bnx2x_virtf *vf = NULL; struct pf_vf_bulletin_content *bulletin = NULL; - /* sanity and init */ - rc = bnx2x_vf_ndo_prep(bp, vfidx, &vf, &bulletin); - if (rc) - return rc; if (!is_valid_ether_addr(mac)) { BNX2X_ERR("mac address invalid\n"); return -EINVAL; } + /* sanity and init */ + rc = bnx2x_vf_op_prep(bp, vfidx, &vf, &bulletin, true); + if (rc) + return rc; + + mutex_lock(&bp->vfdb->bulletin_mutex); + /* update PF's copy of the VF's bulletin. Will no longer accept mac * configuration requests from vf unless match this mac */ @@ -2652,6 +2766,10 @@ int bnx2x_set_vf_mac(struct net_device *dev, int vfidx, u8 *mac) /* Post update on VF's bulletin board */ rc = bnx2x_post_vf_bulletin(bp, vfidx); + + /* release lock before checking return code */ + mutex_unlock(&bp->vfdb->bulletin_mutex); + if (rc) { BNX2X_ERR("failed to update VF[%d] bulletin\n", vfidx); return rc; @@ -2716,11 +2834,6 @@ int bnx2x_set_vf_vlan(struct net_device *dev, int vfidx, u16 vlan, u8 qos) unsigned long accept_flags; int rc; - /* sanity and init */ - rc = bnx2x_vf_ndo_prep(bp, vfidx, &vf, &bulletin); - if (rc) - return rc; - if (vlan > 4095) { BNX2X_ERR("illegal vlan value %d\n", vlan); return -EINVAL; @@ -2729,18 +2842,27 @@ int bnx2x_set_vf_vlan(struct net_device *dev, int vfidx, u16 vlan, u8 qos) DP(BNX2X_MSG_IOV, "configuring VF %d with VLAN %d qos %d\n", vfidx, vlan, 0); + /* sanity and init */ + rc = bnx2x_vf_op_prep(bp, vfidx, &vf, &bulletin, true); + if (rc) + return rc; + /* update PF's copy of the VF's bulletin. No point in posting the vlan * to the VF since it doesn't have anything to do with it. But it useful * to store it here in case the VF is not up yet and we can only * configure the vlan later when it does. Treat vlan id 0 as remove the * Host tag. */ + mutex_lock(&bp->vfdb->bulletin_mutex); + if (vlan > 0) bulletin->valid_bitmap |= 1 << VLAN_VALID; else bulletin->valid_bitmap &= ~(1 << VLAN_VALID); bulletin->vlan = vlan; + mutex_unlock(&bp->vfdb->bulletin_mutex); + /* is vf initialized and queue set up? */ if (vf->state != VF_ENABLED || bnx2x_get_q_logical_state(bp, &bnx2x_leading_vfq(vf, sp_obj)) != @@ -2850,10 +2972,9 @@ out: * entire bulletin board excluding the crc field itself. Use the length field * as the Bulletin Board was posted by a PF with possibly a different version * from the vf which will sample it. Therefore, the length is computed by the - * PF and the used blindly by the VF. + * PF and then used blindly by the VF. */ -u32 bnx2x_crc_vf_bulletin(struct bnx2x *bp, - struct pf_vf_bulletin_content *bulletin) +u32 bnx2x_crc_vf_bulletin(struct pf_vf_bulletin_content *bulletin) { return crc32(BULLETIN_CRC_SEED, ((u8 *)bulletin) + sizeof(bulletin->crc), @@ -2863,47 +2984,74 @@ u32 bnx2x_crc_vf_bulletin(struct bnx2x *bp, /* Check for new posts on the bulletin board */ enum sample_bulletin_result bnx2x_sample_bulletin(struct bnx2x *bp) { - struct pf_vf_bulletin_content bulletin = bp->pf2vf_bulletin->content; + struct pf_vf_bulletin_content *bulletin; int attempts; - /* bulletin board hasn't changed since last sample */ - if (bp->old_bulletin.version == bulletin.version) - return PFVF_BULLETIN_UNCHANGED; + /* sampling structure in mid post may result with corrupted data + * validate crc to ensure coherency. + */ + for (attempts = 0; attempts < BULLETIN_ATTEMPTS; attempts++) { + u32 crc; - /* validate crc of new bulletin board */ - if (bp->old_bulletin.version != bp->pf2vf_bulletin->content.version) { - /* sampling structure in mid post may result with corrupted data - * validate crc to ensure coherency. - */ - for (attempts = 0; attempts < BULLETIN_ATTEMPTS; attempts++) { - bulletin = bp->pf2vf_bulletin->content; - if (bulletin.crc == bnx2x_crc_vf_bulletin(bp, - &bulletin)) - break; - BNX2X_ERR("bad crc on bulletin board. Contained %x computed %x\n", - bulletin.crc, - bnx2x_crc_vf_bulletin(bp, &bulletin)); - } - if (attempts >= BULLETIN_ATTEMPTS) { - BNX2X_ERR("pf to vf bulletin board crc was wrong %d consecutive times. Aborting\n", - attempts); - return PFVF_BULLETIN_CRC_ERR; - } + /* sample the bulletin board */ + memcpy(&bp->shadow_bulletin, bp->pf2vf_bulletin, + sizeof(union pf_vf_bulletin)); + + crc = bnx2x_crc_vf_bulletin(&bp->shadow_bulletin.content); + + if (bp->shadow_bulletin.content.crc == crc) + break; + + BNX2X_ERR("bad crc on bulletin board. Contained %x computed %x\n", + bp->shadow_bulletin.content.crc, crc); + } + + if (attempts >= BULLETIN_ATTEMPTS) { + BNX2X_ERR("pf to vf bulletin board crc was wrong %d consecutive times. Aborting\n", + attempts); + return PFVF_BULLETIN_CRC_ERR; } + bulletin = &bp->shadow_bulletin.content; + + /* bulletin board hasn't changed since last sample */ + if (bp->old_bulletin.version == bulletin->version) + return PFVF_BULLETIN_UNCHANGED; /* the mac address in bulletin board is valid and is new */ - if (bulletin.valid_bitmap & 1 << MAC_ADDR_VALID && - !ether_addr_equal(bulletin.mac, bp->old_bulletin.mac)) { + if (bulletin->valid_bitmap & 1 << MAC_ADDR_VALID && + !ether_addr_equal(bulletin->mac, bp->old_bulletin.mac)) { /* update new mac to net device */ - memcpy(bp->dev->dev_addr, bulletin.mac, ETH_ALEN); + memcpy(bp->dev->dev_addr, bulletin->mac, ETH_ALEN); + } + + if (bulletin->valid_bitmap & (1 << LINK_VALID)) { + DP(BNX2X_MSG_IOV, "link update speed %d flags %x\n", + bulletin->link_speed, bulletin->link_flags); + + bp->vf_link_vars.line_speed = bulletin->link_speed; + bp->vf_link_vars.link_report_flags = 0; + /* Link is down */ + if (bulletin->link_flags & VFPF_LINK_REPORT_LINK_DOWN) + __set_bit(BNX2X_LINK_REPORT_LINK_DOWN, + &bp->vf_link_vars.link_report_flags); + /* Full DUPLEX */ + if (bulletin->link_flags & VFPF_LINK_REPORT_FULL_DUPLEX) + __set_bit(BNX2X_LINK_REPORT_FD, + &bp->vf_link_vars.link_report_flags); + /* Rx Flow Control is ON */ + if (bulletin->link_flags & VFPF_LINK_REPORT_RX_FC_ON) + __set_bit(BNX2X_LINK_REPORT_RX_FC_ON, + &bp->vf_link_vars.link_report_flags); + /* Tx Flow Control is ON */ + if (bulletin->link_flags & VFPF_LINK_REPORT_TX_FC_ON) + __set_bit(BNX2X_LINK_REPORT_TX_FC_ON, + &bp->vf_link_vars.link_report_flags); + __bnx2x_link_report(bp); } - /* the vlan in bulletin board is valid and is new */ - if (bulletin.valid_bitmap & 1 << VLAN_VALID) - memcpy(&bulletin.vlan, &bp->old_bulletin.vlan, VLAN_HLEN); - /* copy new bulletin board to bp */ - bp->old_bulletin = bulletin; + memcpy(&bp->old_bulletin, bulletin, + sizeof(struct pf_vf_bulletin_content)); return PFVF_BULLETIN_UPDATED; } @@ -2948,6 +3096,8 @@ int bnx2x_vf_pci_alloc(struct bnx2x *bp) if (!bp->pf2vf_bulletin) goto alloc_mem_err; + bnx2x_vf_bulletin_finalize(&bp->pf2vf_bulletin->content, true); + return 0; alloc_mem_err: diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.h index 96c575e147a5..ca1055f3d8af 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.h @@ -126,7 +126,11 @@ struct bnx2x_virtf { #define VF_CACHE_LINE 0x0010 #define VF_CFG_VLAN 0x0020 #define VF_CFG_STATS_COALESCE 0x0040 - +#define VF_CFG_EXT_BULLETIN 0x0080 + u8 link_cfg; /* IFLA_VF_LINK_STATE_AUTO + * IFLA_VF_LINK_STATE_ENABLE + * IFLA_VF_LINK_STATE_DISABLE + */ u8 state; #define VF_FREE 0 /* VF ready to be acquired holds no resc */ #define VF_ACQUIRED 1 /* VF acquired, but not initialized */ @@ -295,22 +299,22 @@ struct bnx2x_vfdb { #define BP_VFDB(bp) ((bp)->vfdb) /* vf array */ struct bnx2x_virtf *vfs; -#define BP_VF(bp, idx) (&((bp)->vfdb->vfs[(idx)])) -#define bnx2x_vf(bp, idx, var) ((bp)->vfdb->vfs[(idx)].var) +#define BP_VF(bp, idx) (&((bp)->vfdb->vfs[idx])) +#define bnx2x_vf(bp, idx, var) ((bp)->vfdb->vfs[idx].var) /* queue array - for all vfs */ struct bnx2x_vf_queue *vfqs; /* vf HW contexts */ struct hw_dma context[BNX2X_VF_CIDS/ILT_PAGE_CIDS]; -#define BP_VF_CXT_PAGE(bp, i) (&(bp)->vfdb->context[(i)]) +#define BP_VF_CXT_PAGE(bp, i) (&(bp)->vfdb->context[i]) /* SR-IOV information */ struct bnx2x_sriov sriov; struct hw_dma mbx_dma; #define BP_VF_MBX_DMA(bp) (&((bp)->vfdb->mbx_dma)) struct bnx2x_vf_mbx mbxs[BNX2X_MAX_NUM_OF_VFS]; -#define BP_VF_MBX(bp, vfid) (&((bp)->vfdb->mbxs[(vfid)])) +#define BP_VF_MBX(bp, vfid) (&((bp)->vfdb->mbxs[vfid])) struct hw_dma bulletin_dma; #define BP_VF_BULLETIN_DMA(bp) (&((bp)->vfdb->bulletin_dma)) @@ -336,6 +340,9 @@ struct bnx2x_vfdb { /* sp_rtnl synchronization */ struct mutex event_mutex; u64 event_occur; + + /* bulletin board update synchronization */ + struct mutex bulletin_mutex; }; /* queue access */ @@ -467,9 +474,10 @@ void bnx2x_vf_handle_flr_event(struct bnx2x *bp); bool bnx2x_tlv_supported(u16 tlvtype); -u32 bnx2x_crc_vf_bulletin(struct bnx2x *bp, - struct pf_vf_bulletin_content *bulletin); +u32 bnx2x_crc_vf_bulletin(struct pf_vf_bulletin_content *bulletin); int bnx2x_post_vf_bulletin(struct bnx2x *bp, int vf); +void bnx2x_vf_bulletin_finalize(struct pf_vf_bulletin_content *bulletin, + bool support_long); enum sample_bulletin_result bnx2x_sample_bulletin(struct bnx2x *bp); @@ -520,6 +528,11 @@ void bnx2x_iov_task(struct work_struct *work); void bnx2x_schedule_iov_task(struct bnx2x *bp, enum bnx2x_iov_flag flag); +void bnx2x_iov_link_update(struct bnx2x *bp); +int bnx2x_iov_link_update_vf(struct bnx2x *bp, int idx); + +int bnx2x_set_vf_link_state(struct net_device *dev, int vf, int link_state); + #else /* CONFIG_BNX2X_SRIOV */ static inline void bnx2x_iov_set_queue_sp_obj(struct bnx2x *bp, int vf_cid, @@ -579,6 +592,14 @@ static inline void bnx2x_iov_channel_down(struct bnx2x *bp) {} static inline void bnx2x_iov_task(struct work_struct *work) {} static inline void bnx2x_schedule_iov_task(struct bnx2x *bp, enum bnx2x_iov_flag flag) {} +static inline void bnx2x_iov_link_update(struct bnx2x *bp) {} +static inline int bnx2x_iov_link_update_vf(struct bnx2x *bp, int idx) {return 0; } + +static inline int bnx2x_set_vf_link_state(struct net_device *dev, int vf, + int link_state) {return 0; } +struct pf_vf_bulletin_content; +static inline void bnx2x_vf_bulletin_finalize(struct pf_vf_bulletin_content *bulletin, + bool support_long) {} #endif /* CONFIG_BNX2X_SRIOV */ #endif /* bnx2x_sriov.h */ diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_vfpf.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_vfpf.c index d712d0ddd719..54e0427a9ee6 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_vfpf.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_vfpf.c @@ -251,6 +251,9 @@ int bnx2x_vfpf_acquire(struct bnx2x *bp, u8 tx_count, u8 rx_count) bnx2x_add_tlv(bp, req, req->first_tlv.tl.length, CHANNEL_TLV_PHYS_PORT_ID, sizeof(struct channel_tlv)); + /* Bulletin support for bulletin board with length > legacy length */ + req->vfdev_info.caps |= VF_CAP_SUPPORT_EXT_BULLETIN; + /* add list termination tlv */ bnx2x_add_tlv(bp, req, req->first_tlv.tl.length + sizeof(struct channel_tlv), @@ -1232,6 +1235,41 @@ static void bnx2x_vf_mbx_acquire_resp(struct bnx2x *bp, struct bnx2x_virtf *vf, bnx2x_vf_mbx_resp_send_msg(bp, vf, vfop_status); } +static bool bnx2x_vf_mbx_is_windows_vm(struct bnx2x *bp, + struct vfpf_acquire_tlv *acquire) +{ + /* Windows driver does one of three things: + * 1. Old driver doesn't have bulletin board address set. + * 2. 'Middle' driver sends mc_num == 32. + * 3. New driver sets the OS field. + */ + if (!acquire->bulletin_addr || + acquire->resc_request.num_mc_filters == 32 || + ((acquire->vfdev_info.vf_os & VF_OS_MASK) == + VF_OS_WINDOWS)) + return true; + + return false; +} + +static int bnx2x_vf_mbx_acquire_chk_dorq(struct bnx2x *bp, + struct bnx2x_virtf *vf, + struct bnx2x_vf_mbx *mbx) +{ + /* Linux drivers which correctly set the doorbell size also + * send a physical port request + */ + if (bnx2x_search_tlv_list(bp, &mbx->msg->req, + CHANNEL_TLV_PHYS_PORT_ID)) + return 0; + + /* Issue does not exist in windows VMs */ + if (bnx2x_vf_mbx_is_windows_vm(bp, &mbx->msg->req.acquire)) + return 0; + + return -EOPNOTSUPP; +} + static void bnx2x_vf_mbx_acquire(struct bnx2x *bp, struct bnx2x_virtf *vf, struct bnx2x_vf_mbx *mbx) { @@ -1247,12 +1285,32 @@ static void bnx2x_vf_mbx_acquire(struct bnx2x *bp, struct bnx2x_virtf *vf, acquire->resc_request.num_vlan_filters, acquire->resc_request.num_mc_filters); + /* Prevent VFs with old drivers from loading, since they calculate + * CIDs incorrectly requiring a VF-flr [VM reboot] in order to recover + * while being upgraded. + */ + rc = bnx2x_vf_mbx_acquire_chk_dorq(bp, vf, mbx); + if (rc) { + DP(BNX2X_MSG_IOV, + "VF [%d] - Can't support acquire request due to doorbell mismatch. Please update VM driver\n", + vf->abs_vfid); + goto out; + } + /* acquire the resources */ rc = bnx2x_vf_acquire(bp, vf, &acquire->resc_request); /* store address of vf's bulletin board */ vf->bulletin_map = acquire->bulletin_addr; + if (acquire->vfdev_info.caps & VF_CAP_SUPPORT_EXT_BULLETIN) { + DP(BNX2X_MSG_IOV, "VF[%d] supports long bulletin boards\n", + vf->abs_vfid); + vf->cfg_flags |= VF_CFG_EXT_BULLETIN; + } else { + vf->cfg_flags &= ~VF_CFG_EXT_BULLETIN; + } +out: /* response */ bnx2x_vf_mbx_acquire_resp(bp, vf, mbx, rc); } @@ -1273,6 +1331,10 @@ static void bnx2x_vf_mbx_init_vf(struct bnx2x *bp, struct bnx2x_virtf *vf, if (init->flags & VFPF_INIT_FLG_STATS_COALESCE) vf->cfg_flags |= VF_CFG_STATS_COALESCE; + /* Update VF's view of link state */ + if (vf->cfg_flags & VF_CFG_EXT_BULLETIN) + bnx2x_iov_link_update_vf(bp, vf->index); + /* response */ bnx2x_vf_mbx_resp(bp, vf, rc); } @@ -2007,6 +2069,17 @@ void bnx2x_vf_mbx(struct bnx2x *bp) } } +void bnx2x_vf_bulletin_finalize(struct pf_vf_bulletin_content *bulletin, + bool support_long) +{ + /* Older VFs contain a bug where they can't check CRC for bulletin + * boards of length greater than legacy size. + */ + bulletin->length = support_long ? BULLETIN_CONTENT_SIZE : + BULLETIN_CONTENT_LEGACY_SIZE; + bulletin->crc = bnx2x_crc_vf_bulletin(bulletin); +} + /* propagate local bulletin board to vf */ int bnx2x_post_vf_bulletin(struct bnx2x *bp, int vf) { @@ -2023,8 +2096,9 @@ int bnx2x_post_vf_bulletin(struct bnx2x *bp, int vf) /* increment bulletin board version and compute crc */ bulletin->version++; - bulletin->length = BULLETIN_CONTENT_SIZE; - bulletin->crc = bnx2x_crc_vf_bulletin(bp, bulletin); + bnx2x_vf_bulletin_finalize(bulletin, + (bnx2x_vf(bp, vf, cfg_flags) & + VF_CFG_EXT_BULLETIN) ? true : false); /* propagate bulletin board via dmae to vm memory */ rc = bnx2x_copy32_vf_dmae(bp, false, pf_addr, diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_vfpf.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_vfpf.h index e21e706762c9..15670c499a20 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_vfpf.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_vfpf.h @@ -65,6 +65,7 @@ struct hw_sb_info { #define VFPF_RX_MASK_ACCEPT_ALL_MULTICAST 0x00000008 #define VFPF_RX_MASK_ACCEPT_BROADCAST 0x00000010 #define BULLETIN_CONTENT_SIZE (sizeof(struct pf_vf_bulletin_content)) +#define BULLETIN_CONTENT_LEGACY_SIZE (32) #define BULLETIN_ATTEMPTS 5 /* crc failures before throwing towel */ #define BULLETIN_CRC_SEED 0 @@ -117,7 +118,15 @@ struct vfpf_acquire_tlv { /* the following fields are for debug purposes */ u8 vf_id; /* ME register value */ u8 vf_os; /* e.g. Linux, W2K8 */ - u8 padding[2]; +#define VF_OS_SUBVERSION_MASK (0x1f) +#define VF_OS_MASK (0xe0) +#define VF_OS_SHIFT (5) +#define VF_OS_UNDEFINED (0 << VF_OS_SHIFT) +#define VF_OS_WINDOWS (1 << VF_OS_SHIFT) + + u8 padding; + u8 caps; +#define VF_CAP_SUPPORT_EXT_BULLETIN (1 << 0) } vfdev_info; struct vf_pf_resc_request resc_request; @@ -393,11 +402,23 @@ struct pf_vf_bulletin_content { * to attempt to send messages on the * channel after this bit is set */ +#define LINK_VALID 3 /* alert the VF thet a new link status + * update is available for it + */ u8 mac[ETH_ALEN]; u8 mac_padding[2]; u16 vlan; u8 vlan_padding[6]; + + u16 link_speed; /* Effective line speed */ + u8 link_speed_padding[6]; + u32 link_flags; /* VFPF_LINK_REPORT_XXX flags */ +#define VFPF_LINK_REPORT_LINK_DOWN (1 << 0) +#define VFPF_LINK_REPORT_FULL_DUPLEX (1 << 1) +#define VFPF_LINK_REPORT_RX_FC_ON (1 << 2) +#define VFPF_LINK_REPORT_TX_FC_ON (1 << 3) + u8 link_flags_padding[4]; }; union pf_vf_bulletin { diff --git a/drivers/net/ethernet/broadcom/cnic.c b/drivers/net/ethernet/broadcom/cnic.c index 8244e2b14bb4..27861a6c7ca5 100644 --- a/drivers/net/ethernet/broadcom/cnic.c +++ b/drivers/net/ethernet/broadcom/cnic.c @@ -1,13 +1,15 @@ -/* cnic.c: Broadcom CNIC core network driver. +/* cnic.c: QLogic CNIC core network driver. * * Copyright (c) 2006-2014 Broadcom Corporation + * Copyright (c) 2014 QLogic Corporation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation. * * Original skeleton written by: John(Zongxi) Chen (zongxi@broadcom.com) - * Modified and maintained by: Michael Chan <mchan@broadcom.com> + * Previously modified and maintained by: Michael Chan <mchan@broadcom.com> + * Maintained By: Dept-HSGLinuxNICDev@qlogic.com */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt @@ -56,11 +58,11 @@ #define CNIC_MODULE_NAME "cnic" static char version[] = - "Broadcom NetXtreme II CNIC Driver " CNIC_MODULE_NAME " v" CNIC_MODULE_VERSION " (" CNIC_MODULE_RELDATE ")\n"; + "QLogic NetXtreme II CNIC Driver " CNIC_MODULE_NAME " v" CNIC_MODULE_VERSION " (" CNIC_MODULE_RELDATE ")\n"; MODULE_AUTHOR("Michael Chan <mchan@broadcom.com> and John(Zongxi) " "Chen (zongxi@broadcom.com"); -MODULE_DESCRIPTION("Broadcom NetXtreme II CNIC Driver"); +MODULE_DESCRIPTION("QLogic NetXtreme II CNIC Driver"); MODULE_LICENSE("GPL"); MODULE_VERSION(CNIC_MODULE_VERSION); diff --git a/drivers/net/ethernet/broadcom/cnic.h b/drivers/net/ethernet/broadcom/cnic.h index d535ae4228b4..4baea81bae7a 100644 --- a/drivers/net/ethernet/broadcom/cnic.h +++ b/drivers/net/ethernet/broadcom/cnic.h @@ -1,6 +1,7 @@ -/* cnic.h: Broadcom CNIC core network driver. +/* cnic.h: QLogic CNIC core network driver. * * Copyright (c) 2006-2014 Broadcom Corporation + * Copyright (c) 2014 QLogic Corporation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/drivers/net/ethernet/broadcom/cnic_defs.h b/drivers/net/ethernet/broadcom/cnic_defs.h index dcbca6997e8f..b38499774071 100644 --- a/drivers/net/ethernet/broadcom/cnic_defs.h +++ b/drivers/net/ethernet/broadcom/cnic_defs.h @@ -1,7 +1,8 @@ -/* cnic.c: Broadcom CNIC core network driver. +/* cnic.c: QLogic CNIC core network driver. * * Copyright (c) 2006-2014 Broadcom Corporation + * Copyright (c) 2014 QLogic Corporation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/drivers/net/ethernet/broadcom/cnic_if.h b/drivers/net/ethernet/broadcom/cnic_if.h index 5f4d5573a73d..8bb36c1c4d68 100644 --- a/drivers/net/ethernet/broadcom/cnic_if.h +++ b/drivers/net/ethernet/broadcom/cnic_if.h @@ -1,6 +1,7 @@ -/* cnic_if.h: Broadcom CNIC core network driver. +/* cnic_if.h: QLogic CNIC core network driver. * * Copyright (c) 2006-2014 Broadcom Corporation + * Copyright (c) 2014 QLogic Corporation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/drivers/net/ethernet/broadcom/genet/Makefile b/drivers/net/ethernet/broadcom/genet/Makefile index 31f55a90a197..9b6885efa9e7 100644 --- a/drivers/net/ethernet/broadcom/genet/Makefile +++ b/drivers/net/ethernet/broadcom/genet/Makefile @@ -1,2 +1,2 @@ obj-$(CONFIG_BCMGENET) += genet.o -genet-objs := bcmgenet.o bcmmii.o +genet-objs := bcmgenet.o bcmmii.o bcmgenet_wol.o diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.c b/drivers/net/ethernet/broadcom/genet/bcmgenet.c index 4e615debe472..ce455aed5a2f 100644 --- a/drivers/net/ethernet/broadcom/genet/bcmgenet.c +++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.c @@ -6,15 +6,6 @@ * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License 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. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #define pr_fmt(fmt) "bcmgenet: " fmt @@ -79,13 +70,13 @@ TOTAL_DESC * DMA_DESC_SIZE) static inline void dmadesc_set_length_status(struct bcmgenet_priv *priv, - void __iomem *d, u32 value) + void __iomem *d, u32 value) { __raw_writel(value, d + DMA_DESC_LENGTH_STATUS); } static inline u32 dmadesc_get_length_status(struct bcmgenet_priv *priv, - void __iomem *d) + void __iomem *d) { return __raw_readl(d + DMA_DESC_LENGTH_STATUS); } @@ -98,7 +89,7 @@ static inline void dmadesc_set_addr(struct bcmgenet_priv *priv, /* Register writes to GISB bus can take couple hundred nanoseconds * and are done for each packet, save these expensive writes unless - * the platform is explicitely configured for 64-bits/LPAE. + * the platform is explicitly configured for 64-bits/LPAE. */ #ifdef CONFIG_PHYS_ADDR_T_64BIT if (priv->hw_params->flags & GENET_HAS_40BITS) @@ -108,7 +99,7 @@ static inline void dmadesc_set_addr(struct bcmgenet_priv *priv, /* Combined address + length/status setter */ static inline void dmadesc_set(struct bcmgenet_priv *priv, - void __iomem *d, dma_addr_t addr, u32 val) + void __iomem *d, dma_addr_t addr, u32 val) { dmadesc_set_length_status(priv, d, val); dmadesc_set_addr(priv, d, addr); @@ -123,7 +114,7 @@ static inline dma_addr_t dmadesc_get_addr(struct bcmgenet_priv *priv, /* Register writes to GISB bus can take couple hundred nanoseconds * and are done for each packet, save these expensive writes unless - * the platform is explicitely configured for 64-bits/LPAE. + * the platform is explicitly configured for 64-bits/LPAE. */ #ifdef CONFIG_PHYS_ADDR_T_64BIT if (priv->hw_params->flags & GENET_HAS_40BITS) @@ -242,7 +233,7 @@ static inline struct bcmgenet_priv *dev_to_priv(struct device *dev) } static inline u32 bcmgenet_tdma_readl(struct bcmgenet_priv *priv, - enum dma_reg r) + enum dma_reg r) { return __raw_readl(priv->base + GENET_TDMA_REG_OFF + DMA_RINGS_SIZE + bcmgenet_dma_regs[r]); @@ -256,7 +247,7 @@ static inline void bcmgenet_tdma_writel(struct bcmgenet_priv *priv, } static inline u32 bcmgenet_rdma_readl(struct bcmgenet_priv *priv, - enum dma_reg r) + enum dma_reg r) { return __raw_readl(priv->base + GENET_RDMA_REG_OFF + DMA_RINGS_SIZE + bcmgenet_dma_regs[r]); @@ -333,8 +324,8 @@ static const u8 genet_dma_ring_regs_v123[] = { static const u8 *genet_dma_ring_regs; static inline u32 bcmgenet_tdma_ring_readl(struct bcmgenet_priv *priv, - unsigned int ring, - enum dma_ring_reg r) + unsigned int ring, + enum dma_ring_reg r) { return __raw_readl(priv->base + GENET_TDMA_REG_OFF + (DMA_RING_SIZE * ring) + @@ -342,9 +333,8 @@ static inline u32 bcmgenet_tdma_ring_readl(struct bcmgenet_priv *priv, } static inline void bcmgenet_tdma_ring_writel(struct bcmgenet_priv *priv, - unsigned int ring, - u32 val, - enum dma_ring_reg r) + unsigned int ring, u32 val, + enum dma_ring_reg r) { __raw_writel(val, priv->base + GENET_TDMA_REG_OFF + (DMA_RING_SIZE * ring) + @@ -352,8 +342,8 @@ static inline void bcmgenet_tdma_ring_writel(struct bcmgenet_priv *priv, } static inline u32 bcmgenet_rdma_ring_readl(struct bcmgenet_priv *priv, - unsigned int ring, - enum dma_ring_reg r) + unsigned int ring, + enum dma_ring_reg r) { return __raw_readl(priv->base + GENET_RDMA_REG_OFF + (DMA_RING_SIZE * ring) + @@ -361,9 +351,8 @@ static inline u32 bcmgenet_rdma_ring_readl(struct bcmgenet_priv *priv, } static inline void bcmgenet_rdma_ring_writel(struct bcmgenet_priv *priv, - unsigned int ring, - u32 val, - enum dma_ring_reg r) + unsigned int ring, u32 val, + enum dma_ring_reg r) { __raw_writel(val, priv->base + GENET_RDMA_REG_OFF + (DMA_RING_SIZE * ring) + @@ -371,7 +360,7 @@ static inline void bcmgenet_rdma_ring_writel(struct bcmgenet_priv *priv, } static int bcmgenet_get_settings(struct net_device *dev, - struct ethtool_cmd *cmd) + struct ethtool_cmd *cmd) { struct bcmgenet_priv *priv = netdev_priv(dev); @@ -385,7 +374,7 @@ static int bcmgenet_get_settings(struct net_device *dev, } static int bcmgenet_set_settings(struct net_device *dev, - struct ethtool_cmd *cmd) + struct ethtool_cmd *cmd) { struct bcmgenet_priv *priv = netdev_priv(dev); @@ -458,7 +447,7 @@ static int bcmgenet_set_tx_csum(struct net_device *dev, } static int bcmgenet_set_features(struct net_device *dev, - netdev_features_t features) + netdev_features_t features) { netdev_features_t changed = features ^ dev->features; netdev_features_t wanted = dev->wanted_features; @@ -625,12 +614,11 @@ static const struct bcmgenet_stats bcmgenet_gstrings_stats[] = { #define BCMGENET_STATS_LEN ARRAY_SIZE(bcmgenet_gstrings_stats) static void bcmgenet_get_drvinfo(struct net_device *dev, - struct ethtool_drvinfo *info) + struct ethtool_drvinfo *info) { strlcpy(info->driver, "bcmgenet", sizeof(info->driver)); strlcpy(info->version, "v2.0", sizeof(info->version)); info->n_stats = BCMGENET_STATS_LEN; - } static int bcmgenet_get_sset_count(struct net_device *dev, int string_set) @@ -643,8 +631,8 @@ static int bcmgenet_get_sset_count(struct net_device *dev, int string_set) } } -static void bcmgenet_get_strings(struct net_device *dev, - u32 stringset, u8 *data) +static void bcmgenet_get_strings(struct net_device *dev, u32 stringset, + u8 *data) { int i; @@ -652,8 +640,8 @@ static void bcmgenet_get_strings(struct net_device *dev, case ETH_SS_STATS: for (i = 0; i < BCMGENET_STATS_LEN; i++) { memcpy(data + i * ETH_GSTRING_LEN, - bcmgenet_gstrings_stats[i].stat_string, - ETH_GSTRING_LEN); + bcmgenet_gstrings_stats[i].stat_string, + ETH_GSTRING_LEN); } break; } @@ -678,8 +666,8 @@ static void bcmgenet_update_mib_counters(struct bcmgenet_priv *priv) case BCMGENET_STAT_RUNT: if (s->type != BCMGENET_STAT_MIB_RX) offset = BCMGENET_STAT_OFFSET; - val = bcmgenet_umac_readl(priv, UMAC_MIB_START + - j + offset); + val = bcmgenet_umac_readl(priv, + UMAC_MIB_START + j + offset); break; case BCMGENET_STAT_MISC: val = bcmgenet_umac_readl(priv, s->reg_offset); @@ -696,8 +684,8 @@ static void bcmgenet_update_mib_counters(struct bcmgenet_priv *priv) } static void bcmgenet_get_ethtool_stats(struct net_device *dev, - struct ethtool_stats *stats, - u64 *data) + struct ethtool_stats *stats, + u64 *data) { struct bcmgenet_priv *priv = netdev_priv(dev); int i; @@ -730,6 +718,8 @@ static struct ethtool_ops bcmgenet_ethtool_ops = { .get_link = ethtool_op_get_link, .get_msglevel = bcmgenet_get_msglevel, .set_msglevel = bcmgenet_set_msglevel, + .get_wol = bcmgenet_get_wol, + .set_wol = bcmgenet_set_wol, }; /* Power down the unimac, based on mode. */ @@ -743,6 +733,10 @@ static void bcmgenet_power_down(struct bcmgenet_priv *priv, phy_detach(priv->phydev); break; + case GENET_POWER_WOL_MAGIC: + bcmgenet_wol_power_down_cfg(priv, mode); + break; + case GENET_POWER_PASSIVE: /* Power down LED */ bcmgenet_mii_reset(priv->dev); @@ -759,7 +753,7 @@ static void bcmgenet_power_down(struct bcmgenet_priv *priv, } static void bcmgenet_power_up(struct bcmgenet_priv *priv, - enum bcmgenet_power_mode mode) + enum bcmgenet_power_mode mode) { u32 reg; @@ -777,6 +771,9 @@ static void bcmgenet_power_up(struct bcmgenet_priv *priv, /* enable APD */ reg |= EXT_PWR_DN_EN_LD; break; + case GENET_POWER_WOL_MAGIC: + bcmgenet_wol_power_up_cfg(priv, mode); + return; default: break; } @@ -841,37 +838,37 @@ static inline void bcmgenet_tx_ring16_int_disable(struct bcmgenet_priv *priv, struct bcmgenet_tx_ring *ring) { bcmgenet_intrl2_0_writel(priv, - UMAC_IRQ_TXDMA_BDONE | UMAC_IRQ_TXDMA_PDONE, - INTRL2_CPU_MASK_SET); + UMAC_IRQ_TXDMA_BDONE | UMAC_IRQ_TXDMA_PDONE, + INTRL2_CPU_MASK_SET); } static inline void bcmgenet_tx_ring16_int_enable(struct bcmgenet_priv *priv, struct bcmgenet_tx_ring *ring) { bcmgenet_intrl2_0_writel(priv, - UMAC_IRQ_TXDMA_BDONE | UMAC_IRQ_TXDMA_PDONE, - INTRL2_CPU_MASK_CLEAR); + UMAC_IRQ_TXDMA_BDONE | UMAC_IRQ_TXDMA_PDONE, + INTRL2_CPU_MASK_CLEAR); } static inline void bcmgenet_tx_ring_int_enable(struct bcmgenet_priv *priv, - struct bcmgenet_tx_ring *ring) + struct bcmgenet_tx_ring *ring) { - bcmgenet_intrl2_1_writel(priv, - (1 << ring->index), INTRL2_CPU_MASK_CLEAR); + bcmgenet_intrl2_1_writel(priv, (1 << ring->index), + INTRL2_CPU_MASK_CLEAR); priv->int1_mask &= ~(1 << ring->index); } static inline void bcmgenet_tx_ring_int_disable(struct bcmgenet_priv *priv, struct bcmgenet_tx_ring *ring) { - bcmgenet_intrl2_1_writel(priv, - (1 << ring->index), INTRL2_CPU_MASK_SET); + bcmgenet_intrl2_1_writel(priv, (1 << ring->index), + INTRL2_CPU_MASK_SET); priv->int1_mask |= (1 << ring->index); } /* Unlocked version of the reclaim routine */ static void __bcmgenet_tx_reclaim(struct net_device *dev, - struct bcmgenet_tx_ring *ring) + struct bcmgenet_tx_ring *ring) { struct bcmgenet_priv *priv = netdev_priv(dev); int last_tx_cn, last_c_index, num_tx_bds; @@ -879,7 +876,7 @@ static void __bcmgenet_tx_reclaim(struct net_device *dev, struct netdev_queue *txq; unsigned int c_index; - /* Compute how many buffers are transmited since last xmit call */ + /* Compute how many buffers are transmitted since last xmit call */ c_index = bcmgenet_tdma_ring_readl(priv, ring->index, TDMA_CONS_INDEX); txq = netdev_get_tx_queue(dev, ring->queue); @@ -894,9 +891,9 @@ static void __bcmgenet_tx_reclaim(struct net_device *dev, last_tx_cn = num_tx_bds - last_c_index + c_index; netif_dbg(priv, tx_done, dev, - "%s ring=%d index=%d last_tx_cn=%d last_index=%d\n", - __func__, ring->index, - c_index, last_tx_cn, last_c_index); + "%s ring=%d index=%d last_tx_cn=%d last_index=%d\n", + __func__, ring->index, + c_index, last_tx_cn, last_c_index); /* Reclaim transmitted buffers */ while (last_tx_cn-- > 0) { @@ -904,17 +901,17 @@ static void __bcmgenet_tx_reclaim(struct net_device *dev, if (tx_cb_ptr->skb) { dev->stats.tx_bytes += tx_cb_ptr->skb->len; dma_unmap_single(&dev->dev, - dma_unmap_addr(tx_cb_ptr, dma_addr), - tx_cb_ptr->skb->len, - DMA_TO_DEVICE); + dma_unmap_addr(tx_cb_ptr, dma_addr), + tx_cb_ptr->skb->len, + DMA_TO_DEVICE); bcmgenet_free_cb(tx_cb_ptr); } else if (dma_unmap_addr(tx_cb_ptr, dma_addr)) { dev->stats.tx_bytes += dma_unmap_len(tx_cb_ptr, dma_len); dma_unmap_page(&dev->dev, - dma_unmap_addr(tx_cb_ptr, dma_addr), - dma_unmap_len(tx_cb_ptr, dma_len), - DMA_TO_DEVICE); + dma_unmap_addr(tx_cb_ptr, dma_addr), + dma_unmap_len(tx_cb_ptr, dma_len), + DMA_TO_DEVICE); dma_unmap_addr_set(tx_cb_ptr, dma_addr, 0); } dev->stats.tx_packets++; @@ -934,7 +931,7 @@ static void __bcmgenet_tx_reclaim(struct net_device *dev, } static void bcmgenet_tx_reclaim(struct net_device *dev, - struct bcmgenet_tx_ring *ring) + struct bcmgenet_tx_ring *ring) { unsigned long flags; @@ -1008,11 +1005,11 @@ static int bcmgenet_xmit_single(struct net_device *dev, return 0; } -/* Transmit a SKB fragement */ +/* Transmit a SKB fragment */ static int bcmgenet_xmit_frag(struct net_device *dev, - skb_frag_t *frag, - u16 dma_desc_flags, - struct bcmgenet_tx_ring *ring) + skb_frag_t *frag, + u16 dma_desc_flags, + struct bcmgenet_tx_ring *ring) { struct bcmgenet_priv *priv = netdev_priv(dev); struct device *kdev = &priv->pdev->dev; @@ -1027,11 +1024,11 @@ static int bcmgenet_xmit_frag(struct net_device *dev, tx_cb_ptr->skb = NULL; mapping = skb_frag_dma_map(kdev, frag, 0, - skb_frag_size(frag), DMA_TO_DEVICE); + skb_frag_size(frag), DMA_TO_DEVICE); ret = dma_mapping_error(kdev, mapping); if (ret) { netif_err(priv, tx_err, dev, "%s: Tx DMA map failed\n", - __func__); + __func__); return ret; } @@ -1039,8 +1036,8 @@ static int bcmgenet_xmit_frag(struct net_device *dev, dma_unmap_len_set(tx_cb_ptr, dma_len, frag->size); dmadesc_set(priv, tx_cb_ptr->bd_addr, mapping, - (frag->size << DMA_BUFLENGTH_SHIFT) | dma_desc_flags | - (priv->hw_params->qtag_mask << DMA_TX_QTAG_SHIFT)); + (frag->size << DMA_BUFLENGTH_SHIFT) | dma_desc_flags | + (priv->hw_params->qtag_mask << DMA_TX_QTAG_SHIFT)); ring->free_bds -= 1; @@ -1103,8 +1100,9 @@ static int bcmgenet_put_tx_csum(struct net_device *dev, struct sk_buff *skb) tx_csum_info |= STATUS_TX_CSUM_LV; if (ip_proto == IPPROTO_UDP && ip_ver == ETH_P_IP) tx_csum_info |= STATUS_TX_CSUM_PROTO_UDP; - } else + } else { tx_csum_info = 0; + } status->tx_csum_info = tx_csum_info; } @@ -1144,7 +1142,7 @@ static netdev_tx_t bcmgenet_xmit(struct sk_buff *skb, struct net_device *dev) if (ring->free_bds <= nr_frags + 1) { netif_tx_stop_queue(txq); netdev_err(dev, "%s: tx ring %d full when queue %d awake\n", - __func__, index, ring->queue); + __func__, index, ring->queue); ret = NETDEV_TX_BUSY; goto out; } @@ -1177,8 +1175,9 @@ static netdev_tx_t bcmgenet_xmit(struct sk_buff *skb, struct net_device *dev) /* xmit fragment */ for (i = 0; i < nr_frags; i++) { ret = bcmgenet_xmit_frag(dev, - &skb_shinfo(skb)->frags[i], - (i == nr_frags - 1) ? DMA_EOP : 0, ring); + &skb_shinfo(skb)->frags[i], + (i == nr_frags - 1) ? DMA_EOP : 0, + ring); if (ret) { ret = NETDEV_TX_OK; goto out; @@ -1191,7 +1190,7 @@ static netdev_tx_t bcmgenet_xmit(struct sk_buff *skb, struct net_device *dev) * producer index, now write it down to the hardware */ bcmgenet_tdma_ring_writel(priv, ring->index, - ring->prod_index, TDMA_PROD_INDEX); + ring->prod_index, TDMA_PROD_INDEX); if (ring->free_bds <= (MAX_SKB_FRAGS + 1)) { netif_tx_stop_queue(txq); @@ -1205,16 +1204,14 @@ out: } -static int bcmgenet_rx_refill(struct bcmgenet_priv *priv, - struct enet_cb *cb) +static int bcmgenet_rx_refill(struct bcmgenet_priv *priv, struct enet_cb *cb) { struct device *kdev = &priv->pdev->dev; struct sk_buff *skb; dma_addr_t mapping; int ret; - skb = netdev_alloc_skb(priv->dev, - priv->rx_buf_len + SKB_ALIGNMENT); + skb = netdev_alloc_skb(priv->dev, priv->rx_buf_len + SKB_ALIGNMENT); if (!skb) return -ENOMEM; @@ -1222,12 +1219,12 @@ static int bcmgenet_rx_refill(struct bcmgenet_priv *priv, WARN_ON(cb->skb != NULL); cb->skb = skb; mapping = dma_map_single(kdev, skb->data, - priv->rx_buf_len, DMA_FROM_DEVICE); + priv->rx_buf_len, DMA_FROM_DEVICE); ret = dma_mapping_error(kdev, mapping); if (ret) { bcmgenet_free_cb(cb); netif_err(priv, rx_err, priv->dev, - "%s DMA map failed\n", __func__); + "%s DMA map failed\n", __func__); return ret; } @@ -1262,8 +1259,7 @@ static unsigned int bcmgenet_desc_rx(struct bcmgenet_priv *priv, unsigned int p_index; unsigned int chksum_ok = 0; - p_index = bcmgenet_rdma_ring_readl(priv, - DESC_INDEX, RDMA_PROD_INDEX); + p_index = bcmgenet_rdma_ring_readl(priv, DESC_INDEX, RDMA_PROD_INDEX); p_index &= DMA_P_INDEX_MASK; if (p_index < priv->rx_c_index) @@ -1273,11 +1269,10 @@ static unsigned int bcmgenet_desc_rx(struct bcmgenet_priv *priv, rxpkttoprocess = p_index - priv->rx_c_index; netif_dbg(priv, rx_status, dev, - "RDMA: rxpkttoprocess=%d\n", rxpkttoprocess); + "RDMA: rxpkttoprocess=%d\n", rxpkttoprocess); while ((rxpktprocessed < rxpkttoprocess) && - (rxpktprocessed < budget)) { - + (rxpktprocessed < budget)) { /* Unmap the packet contents such that we can use the * RSV from the 64 bytes descriptor when enabled and save * a 32-bits register read @@ -1285,15 +1280,17 @@ static unsigned int bcmgenet_desc_rx(struct bcmgenet_priv *priv, cb = &priv->rx_cbs[priv->rx_read_ptr]; skb = cb->skb; dma_unmap_single(&dev->dev, dma_unmap_addr(cb, dma_addr), - priv->rx_buf_len, DMA_FROM_DEVICE); + priv->rx_buf_len, DMA_FROM_DEVICE); if (!priv->desc_64b_en) { - dma_length_status = dmadesc_get_length_status(priv, - priv->rx_bds + - (priv->rx_read_ptr * - DMA_DESC_SIZE)); + dma_length_status = + dmadesc_get_length_status(priv, + priv->rx_bds + + (priv->rx_read_ptr * + DMA_DESC_SIZE)); } else { struct status_64 *status; + status = (struct status_64 *)skb->data; dma_length_status = status->length_status; } @@ -1305,9 +1302,9 @@ static unsigned int bcmgenet_desc_rx(struct bcmgenet_priv *priv, len = dma_length_status >> DMA_BUFLENGTH_SHIFT; netif_dbg(priv, rx_status, dev, - "%s: p_ind=%d c_ind=%d read_ptr=%d len_stat=0x%08x\n", - __func__, p_index, priv->rx_c_index, priv->rx_read_ptr, - dma_length_status); + "%s:p_ind=%d c_ind=%d read_ptr=%d len_stat=0x%08x\n", + __func__, p_index, priv->rx_c_index, + priv->rx_read_ptr, dma_length_status); rxpktprocessed++; @@ -1323,7 +1320,7 @@ static unsigned int bcmgenet_desc_rx(struct bcmgenet_priv *priv, if (unlikely(!(dma_flag & DMA_EOP) || !(dma_flag & DMA_SOP))) { netif_err(priv, rx_status, dev, - "Droping fragmented packet!\n"); + "dropping fragmented packet!\n"); dev->stats.rx_dropped++; dev->stats.rx_errors++; dev_kfree_skb_any(cb->skb); @@ -1337,7 +1334,7 @@ static unsigned int bcmgenet_desc_rx(struct bcmgenet_priv *priv, DMA_RX_LG | DMA_RX_RXER))) { netif_err(priv, rx_status, dev, "dma_flag=0x%x\n", - (unsigned int)dma_flag); + (unsigned int)dma_flag); if (dma_flag & DMA_RX_CRC_ERROR) dev->stats.rx_crc_errors++; if (dma_flag & DMA_RX_OV) @@ -1356,7 +1353,7 @@ static unsigned int bcmgenet_desc_rx(struct bcmgenet_priv *priv, } /* error packet */ chksum_ok = (dma_flag & priv->dma_rx_chk_bit) && - priv->desc_rxchk_en; + priv->desc_rxchk_en; skb_put(skb, len); if (priv->desc_64b_en) { @@ -1416,7 +1413,6 @@ static int bcmgenet_alloc_rx_buffers(struct bcmgenet_priv *priv) ret = bcmgenet_rx_refill(priv, cb); if (ret) break; - } return ret; @@ -1432,8 +1428,8 @@ static void bcmgenet_free_rx_buffers(struct bcmgenet_priv *priv) if (dma_unmap_addr(cb, dma_addr)) { dma_unmap_single(&priv->dev->dev, - dma_unmap_addr(cb, dma_addr), - priv->rx_buf_len, DMA_FROM_DEVICE); + dma_unmap_addr(cb, dma_addr), + priv->rx_buf_len, DMA_FROM_DEVICE); dma_unmap_addr_set(cb, dma_addr, 0); } @@ -1442,6 +1438,24 @@ static void bcmgenet_free_rx_buffers(struct bcmgenet_priv *priv) } } +static void umac_enable_set(struct bcmgenet_priv *priv, u32 mask, bool enable) +{ + u32 reg; + + reg = bcmgenet_umac_readl(priv, UMAC_CMD); + if (enable) + reg |= mask; + else + reg &= ~mask; + bcmgenet_umac_writel(priv, reg, UMAC_CMD); + + /* UniMAC stops on a packet boundary, wait for a full-size packet + * to be processed + */ + if (enable == 0) + usleep_range(1000, 2000); +} + static int reset_umac(struct bcmgenet_priv *priv) { struct device *kdev = &priv->pdev->dev; @@ -1467,13 +1481,24 @@ static int reset_umac(struct bcmgenet_priv *priv) if (timeout == 1000) { dev_err(kdev, - "timeout waiting for MAC to come out of resetn\n"); + "timeout waiting for MAC to come out of reset\n"); return -ETIMEDOUT; } return 0; } +static void bcmgenet_intr_disable(struct bcmgenet_priv *priv) +{ + /* Mask all interrupts.*/ + bcmgenet_intrl2_0_writel(priv, 0xFFFFFFFF, INTRL2_CPU_MASK_SET); + bcmgenet_intrl2_0_writel(priv, 0xFFFFFFFF, INTRL2_CPU_CLEAR); + bcmgenet_intrl2_0_writel(priv, 0, INTRL2_CPU_MASK_CLEAR); + bcmgenet_intrl2_1_writel(priv, 0xFFFFFFFF, INTRL2_CPU_MASK_SET); + bcmgenet_intrl2_1_writel(priv, 0xFFFFFFFF, INTRL2_CPU_CLEAR); + bcmgenet_intrl2_1_writel(priv, 0, INTRL2_CPU_MASK_CLEAR); +} + static int init_umac(struct bcmgenet_priv *priv) { struct device *kdev = &priv->pdev->dev; @@ -1489,7 +1514,8 @@ static int init_umac(struct bcmgenet_priv *priv) bcmgenet_umac_writel(priv, 0, UMAC_CMD); /* clear tx/rx counter */ bcmgenet_umac_writel(priv, - MIB_RESET_RX | MIB_RESET_TX | MIB_RESET_RUNT, UMAC_MIB_CTRL); + MIB_RESET_RX | MIB_RESET_TX | MIB_RESET_RUNT, + UMAC_MIB_CTRL); bcmgenet_umac_writel(priv, 0, UMAC_MIB_CTRL); bcmgenet_umac_writel(priv, ENET_MAX_MTU_SIZE, UMAC_MAX_FRAME_LEN); @@ -1502,21 +1528,18 @@ static int init_umac(struct bcmgenet_priv *priv) if (!GENET_IS_V1(priv) && !GENET_IS_V2(priv)) bcmgenet_rbuf_writel(priv, 1, RBUF_TBUF_SIZE_CTRL); - /* Mask all interrupts.*/ - bcmgenet_intrl2_0_writel(priv, 0xFFFFFFFF, INTRL2_CPU_MASK_SET); - bcmgenet_intrl2_0_writel(priv, 0xFFFFFFFF, INTRL2_CPU_CLEAR); - bcmgenet_intrl2_0_writel(priv, 0, INTRL2_CPU_MASK_CLEAR); + bcmgenet_intr_disable(priv); cpu_mask_clear = UMAC_IRQ_RXDMA_BDONE; dev_dbg(kdev, "%s:Enabling RXDMA_BDONE interrupt\n", __func__); - /* Monitor cable plug/unpluged event for internal PHY */ - if (phy_is_internal(priv->phydev)) + /* Monitor cable plug/unplugged event for internal PHY */ + if (phy_is_internal(priv->phydev)) { cpu_mask_clear |= (UMAC_IRQ_LINK_DOWN | UMAC_IRQ_LINK_UP); - else if (priv->ext_phy) + } else if (priv->ext_phy) { cpu_mask_clear |= (UMAC_IRQ_LINK_DOWN | UMAC_IRQ_LINK_UP); - else if (priv->phy_interface == PHY_INTERFACE_MODE_MOCA) { + } else if (priv->phy_interface == PHY_INTERFACE_MODE_MOCA) { reg = bcmgenet_bp_mc_get(priv); reg |= BIT(priv->hw_params->bp_in_en_shift); @@ -1532,8 +1555,7 @@ static int init_umac(struct bcmgenet_priv *priv) if (priv->hw_params->flags & GENET_HAS_MDIO_INTR) cpu_mask_clear |= UMAC_IRQ_MDIO_DONE | UMAC_IRQ_MDIO_ERROR; - bcmgenet_intrl2_0_writel(priv, cpu_mask_clear, - INTRL2_CPU_MASK_CLEAR); + bcmgenet_intrl2_0_writel(priv, cpu_mask_clear, INTRL2_CPU_MASK_CLEAR); /* Enable rx/tx engine.*/ dev_dbg(kdev, "done init umac\n"); @@ -1582,28 +1604,28 @@ static void bcmgenet_init_tx_ring(struct bcmgenet_priv *priv, bcmgenet_tdma_ring_writel(priv, index, 1, DMA_MBUF_DONE_THRESH); /* Disable rate control for now */ bcmgenet_tdma_ring_writel(priv, index, flow_period_val, - TDMA_FLOW_PERIOD); + TDMA_FLOW_PERIOD); /* Unclassified traffic goes to ring 16 */ bcmgenet_tdma_ring_writel(priv, index, - ((size << DMA_RING_SIZE_SHIFT) | RX_BUF_LENGTH), - DMA_RING_BUF_SIZE); + ((size << DMA_RING_SIZE_SHIFT) | + RX_BUF_LENGTH), DMA_RING_BUF_SIZE); first_bd = write_ptr; /* Set start and end address, read and write pointers */ bcmgenet_tdma_ring_writel(priv, index, first_bd * words_per_bd, - DMA_START_ADDR); + DMA_START_ADDR); bcmgenet_tdma_ring_writel(priv, index, first_bd * words_per_bd, - TDMA_READ_PTR); + TDMA_READ_PTR); bcmgenet_tdma_ring_writel(priv, index, first_bd, - TDMA_WRITE_PTR); + TDMA_WRITE_PTR); bcmgenet_tdma_ring_writel(priv, index, end_ptr * words_per_bd - 1, - DMA_END_ADDR); + DMA_END_ADDR); } /* Initialize a RDMA ring */ static int bcmgenet_init_rx_ring(struct bcmgenet_priv *priv, - unsigned int index, unsigned int size) + unsigned int index, unsigned int size) { u32 words_per_bd = WORDS_PER_BD(priv); int ret; @@ -1614,8 +1636,8 @@ static int bcmgenet_init_rx_ring(struct bcmgenet_priv *priv, priv->rx_bd_assign_index = 0; priv->rx_c_index = 0; priv->rx_read_ptr = 0; - priv->rx_cbs = kzalloc(priv->num_rx_bds * sizeof(struct enet_cb), - GFP_KERNEL); + priv->rx_cbs = kcalloc(priv->num_rx_bds, sizeof(struct enet_cb), + GFP_KERNEL); if (!priv->rx_cbs) return -ENOMEM; @@ -1629,14 +1651,15 @@ static int bcmgenet_init_rx_ring(struct bcmgenet_priv *priv, bcmgenet_rdma_ring_writel(priv, index, 0, RDMA_PROD_INDEX); bcmgenet_rdma_ring_writel(priv, index, 0, RDMA_CONS_INDEX); bcmgenet_rdma_ring_writel(priv, index, - ((size << DMA_RING_SIZE_SHIFT) | RX_BUF_LENGTH), - DMA_RING_BUF_SIZE); + ((size << DMA_RING_SIZE_SHIFT) | + RX_BUF_LENGTH), DMA_RING_BUF_SIZE); bcmgenet_rdma_ring_writel(priv, index, 0, DMA_START_ADDR); bcmgenet_rdma_ring_writel(priv, index, - words_per_bd * size - 1, DMA_END_ADDR); + words_per_bd * size - 1, DMA_END_ADDR); bcmgenet_rdma_ring_writel(priv, index, - (DMA_FC_THRESH_LO << DMA_XOFF_THRESHOLD_SHIFT) | - DMA_FC_THRESH_HI, RDMA_XON_XOFF_THRESH); + (DMA_FC_THRESH_LO << + DMA_XOFF_THRESHOLD_SHIFT) | + DMA_FC_THRESH_HI, RDMA_XON_XOFF_THRESH); bcmgenet_rdma_ring_writel(priv, index, 0, RDMA_READ_PTR); return ret; @@ -1683,10 +1706,10 @@ static void bcmgenet_init_multiq(struct net_device *dev) * (ring 16) */ bcmgenet_init_tx_ring(priv, i, priv->hw_params->bds_cnt, - i * priv->hw_params->bds_cnt, - (i + 1) * priv->hw_params->bds_cnt); + i * priv->hw_params->bds_cnt, + (i + 1) * priv->hw_params->bds_cnt); - /* Configure ring as decriptor ring and setup priority */ + /* Configure ring as descriptor ring and setup priority */ ring_cfg |= 1 << i; dma_priority |= ((GENET_Q0_PRIORITY + i) << (GENET_MAX_MQ_CNT + 1) * i); @@ -1752,11 +1775,11 @@ static int bcmgenet_init_dma(struct bcmgenet_priv *priv) /* Init tDma */ bcmgenet_tdma_writel(priv, DMA_MAX_BURST_LENGTH, DMA_SCB_BURST_SIZE); - /* Initialize commont TX ring structures */ + /* Initialize common TX ring structures */ priv->tx_bds = priv->base + priv->hw_params->tdma_offset; priv->num_tx_bds = TOTAL_DESC; - priv->tx_cbs = kzalloc(priv->num_tx_bds * sizeof(struct enet_cb), - GFP_KERNEL); + priv->tx_cbs = kcalloc(priv->num_tx_bds, sizeof(struct enet_cb), + GFP_KERNEL); if (!priv->tx_cbs) { bcmgenet_fini_dma(priv); return -ENOMEM; @@ -1767,8 +1790,9 @@ static int bcmgenet_init_dma(struct bcmgenet_priv *priv) /* initialize special ring 16 */ bcmgenet_init_tx_ring(priv, DESC_INDEX, GENET_DEFAULT_BD_CNT, - priv->hw_params->tx_queues * priv->hw_params->bds_cnt, - TOTAL_DESC); + priv->hw_params->tx_queues * + priv->hw_params->bds_cnt, + TOTAL_DESC); return 0; } @@ -1789,11 +1813,11 @@ static int bcmgenet_poll(struct napi_struct *napi, int budget) priv->rx_c_index += work_done; priv->rx_c_index &= DMA_C_INDEX_MASK; bcmgenet_rdma_ring_writel(priv, DESC_INDEX, - priv->rx_c_index, RDMA_CONS_INDEX); + priv->rx_c_index, RDMA_CONS_INDEX); if (work_done < budget) { napi_complete(napi); - bcmgenet_intrl2_0_writel(priv, - UMAC_IRQ_RXDMA_BDONE, INTRL2_CPU_MASK_CLEAR); + bcmgenet_intrl2_0_writel(priv, UMAC_IRQ_RXDMA_BDONE, + INTRL2_CPU_MASK_CLEAR); } return work_done; @@ -1807,11 +1831,18 @@ static void bcmgenet_irq_task(struct work_struct *work) netif_dbg(priv, intr, priv->dev, "%s\n", __func__); + if (priv->irq0_stat & UMAC_IRQ_MPD_R) { + priv->irq0_stat &= ~UMAC_IRQ_MPD_R; + netif_dbg(priv, wol, priv->dev, + "magic packet detected, waking up\n"); + bcmgenet_power_up(priv, GENET_POWER_WOL_MAGIC); + } + /* Link UP/DOWN event */ if ((priv->hw_params->flags & GENET_HAS_MDIO_INTR) && - (priv->irq0_stat & (UMAC_IRQ_LINK_UP|UMAC_IRQ_LINK_DOWN))) { + (priv->irq0_stat & (UMAC_IRQ_LINK_UP|UMAC_IRQ_LINK_DOWN))) { phy_mac_interrupt(priv->phydev, - priv->irq0_stat & UMAC_IRQ_LINK_UP); + priv->irq0_stat & UMAC_IRQ_LINK_UP); priv->irq0_stat &= ~(UMAC_IRQ_LINK_UP|UMAC_IRQ_LINK_DOWN); } } @@ -1826,11 +1857,11 @@ static irqreturn_t bcmgenet_isr1(int irq, void *dev_id) priv->irq1_stat = bcmgenet_intrl2_1_readl(priv, INTRL2_CPU_STAT) & ~priv->int1_mask; - /* clear inerrupts*/ + /* clear interrupts */ bcmgenet_intrl2_1_writel(priv, priv->irq1_stat, INTRL2_CPU_CLEAR); netif_dbg(priv, intr, priv->dev, - "%s: IRQ=0x%x\n", __func__, priv->irq1_stat); + "%s: IRQ=0x%x\n", __func__, priv->irq1_stat); /* Check the MBDONE interrupts. * packet is done, reclaim descriptors */ @@ -1839,7 +1870,7 @@ static irqreturn_t bcmgenet_isr1(int irq, void *dev_id) for (index = 0; index < 16; index++) { if (priv->irq1_stat & (1 << index)) bcmgenet_tx_reclaim(priv->dev, - &priv->tx_rings[index]); + &priv->tx_rings[index]); } } return IRQ_HANDLED; @@ -1854,11 +1885,11 @@ static irqreturn_t bcmgenet_isr0(int irq, void *dev_id) priv->irq0_stat = bcmgenet_intrl2_0_readl(priv, INTRL2_CPU_STAT) & ~bcmgenet_intrl2_0_readl(priv, INTRL2_CPU_MASK_STATUS); - /* clear inerrupts*/ + /* clear interrupts */ bcmgenet_intrl2_0_writel(priv, priv->irq0_stat, INTRL2_CPU_CLEAR); netif_dbg(priv, intr, priv->dev, - "IRQ=0x%x\n", priv->irq0_stat); + "IRQ=0x%x\n", priv->irq0_stat); if (priv->irq0_stat & (UMAC_IRQ_RXDMA_BDONE | UMAC_IRQ_RXDMA_PDONE)) { /* We use NAPI(software interrupt throttling, if @@ -1866,8 +1897,8 @@ static irqreturn_t bcmgenet_isr0(int irq, void *dev_id) * Disable interrupt, will be enabled in the poll method. */ if (likely(napi_schedule_prep(&priv->napi))) { - bcmgenet_intrl2_0_writel(priv, - UMAC_IRQ_RXDMA_BDONE, INTRL2_CPU_MASK_SET); + bcmgenet_intrl2_0_writel(priv, UMAC_IRQ_RXDMA_BDONE, + INTRL2_CPU_MASK_SET); __napi_schedule(&priv->napi); } } @@ -1888,7 +1919,7 @@ static irqreturn_t bcmgenet_isr0(int irq, void *dev_id) } if ((priv->hw_params->flags & GENET_HAS_MDIO_INTR) && - priv->irq0_stat & (UMAC_IRQ_MDIO_DONE | UMAC_IRQ_MDIO_ERROR)) { + priv->irq0_stat & (UMAC_IRQ_MDIO_DONE | UMAC_IRQ_MDIO_ERROR)) { priv->irq0_stat &= ~(UMAC_IRQ_MDIO_DONE | UMAC_IRQ_MDIO_ERROR); wake_up(&priv->wq); } @@ -1896,6 +1927,15 @@ static irqreturn_t bcmgenet_isr0(int irq, void *dev_id) return IRQ_HANDLED; } +static irqreturn_t bcmgenet_wol_isr(int irq, void *dev_id) +{ + struct bcmgenet_priv *priv = dev_id; + + pm_wakeup_event(&priv->pdev->dev, 0); + + return IRQ_HANDLED; +} + static void bcmgenet_umac_reset(struct bcmgenet_priv *priv) { u32 reg; @@ -1911,7 +1951,7 @@ static void bcmgenet_umac_reset(struct bcmgenet_priv *priv) } static void bcmgenet_set_hw_addr(struct bcmgenet_priv *priv, - unsigned char *addr) + unsigned char *addr) { bcmgenet_umac_writel(priv, (addr[0] << 24) | (addr[1] << 16) | (addr[2] << 8) | addr[3], UMAC_MAC0); @@ -1920,14 +1960,8 @@ static void bcmgenet_set_hw_addr(struct bcmgenet_priv *priv, static int bcmgenet_wol_resume(struct bcmgenet_priv *priv) { - int ret; - /* From WOL-enabled suspend, switch to regular clock */ - clk_disable(priv->clk_wol); - /* init umac registers to synchronize s/w with h/w */ - ret = init_umac(priv); - if (ret) - return ret; + clk_disable_unprepare(priv->clk_wol); phy_init_hw(priv->phydev); /* Speed settings must be restored */ @@ -1972,6 +2006,23 @@ static void bcmgenet_enable_dma(struct bcmgenet_priv *priv, u32 dma_ctrl) bcmgenet_tdma_writel(priv, reg, DMA_CTRL); } +static void bcmgenet_netif_start(struct net_device *dev) +{ + struct bcmgenet_priv *priv = netdev_priv(dev); + + /* Start the network engine */ + napi_enable(&priv->napi); + + umac_enable_set(priv, CMD_TX_EN | CMD_RX_EN, true); + + if (phy_is_internal(priv->phydev)) + bcmgenet_power_up(priv, GENET_POWER_PASSIVE); + + netif_tx_start_all_queues(dev); + + phy_start(priv->phydev); +} + static int bcmgenet_open(struct net_device *dev) { struct bcmgenet_priv *priv = netdev_priv(dev); @@ -1993,18 +2044,14 @@ static int bcmgenet_open(struct net_device *dev) goto err_clk_disable; /* disable ethernet MAC while updating its registers */ + umac_enable_set(priv, CMD_TX_EN | CMD_RX_EN, false); + + /* Make sure we reflect the value of CRC_CMD_FWD */ reg = bcmgenet_umac_readl(priv, UMAC_CMD); - reg &= ~(CMD_TX_EN | CMD_RX_EN); - bcmgenet_umac_writel(priv, reg, UMAC_CMD); + priv->crc_fwd_en = !!(reg & CMD_CRC_FWD); bcmgenet_set_hw_addr(priv, dev->dev_addr); - if (priv->wol_enabled) { - ret = bcmgenet_wol_resume(priv); - if (ret) - return ret; - } - if (phy_is_internal(priv->phydev)) { reg = bcmgenet_ext_readl(priv, EXT_EXT_PWR_MGMT); reg |= EXT_ENERGY_DET_MASK; @@ -2025,37 +2072,20 @@ static int bcmgenet_open(struct net_device *dev) bcmgenet_enable_dma(priv, dma_ctrl); ret = request_irq(priv->irq0, bcmgenet_isr0, IRQF_SHARED, - dev->name, priv); + dev->name, priv); if (ret < 0) { netdev_err(dev, "can't request IRQ %d\n", priv->irq0); goto err_fini_dma; } ret = request_irq(priv->irq1, bcmgenet_isr1, IRQF_SHARED, - dev->name, priv); + dev->name, priv); if (ret < 0) { netdev_err(dev, "can't request IRQ %d\n", priv->irq1); goto err_irq0; } - /* Start the network engine */ - napi_enable(&priv->napi); - - reg = bcmgenet_umac_readl(priv, UMAC_CMD); - reg |= (CMD_TX_EN | CMD_RX_EN); - bcmgenet_umac_writel(priv, reg, UMAC_CMD); - - /* Make sure we reflect the value of CRC_CMD_FWD */ - priv->crc_fwd_en = !!(reg & CMD_CRC_FWD); - - device_set_wakeup_capable(&dev->dev, 1); - - if (phy_is_internal(priv->phydev)) - bcmgenet_power_up(priv, GENET_POWER_PASSIVE); - - netif_tx_start_all_queues(dev); - - phy_start(priv->phydev); + bcmgenet_netif_start(dev); return 0; @@ -2090,8 +2120,7 @@ static int bcmgenet_dma_teardown(struct bcmgenet_priv *priv) } if (timeout == DMA_TIMEOUT_VAL) { - netdev_warn(priv->dev, - "Timed out while disabling TX DMA\n"); + netdev_warn(priv->dev, "Timed out while disabling TX DMA\n"); ret = -ETIMEDOUT; } @@ -2114,41 +2143,47 @@ static int bcmgenet_dma_teardown(struct bcmgenet_priv *priv) } if (timeout == DMA_TIMEOUT_VAL) { - netdev_warn(priv->dev, - "Timed out while disabling RX DMA\n"); - ret = -ETIMEDOUT; + netdev_warn(priv->dev, "Timed out while disabling RX DMA\n"); + ret = -ETIMEDOUT; } return ret; } +static void bcmgenet_netif_stop(struct net_device *dev) +{ + struct bcmgenet_priv *priv = netdev_priv(dev); + + netif_tx_stop_all_queues(dev); + napi_disable(&priv->napi); + phy_stop(priv->phydev); + + bcmgenet_intr_disable(priv); + + /* Wait for pending work items to complete. Since interrupts are + * disabled no new work will be scheduled. + */ + cancel_work_sync(&priv->bcmgenet_irq_work); +} + static int bcmgenet_close(struct net_device *dev) { struct bcmgenet_priv *priv = netdev_priv(dev); int ret; - u32 reg; netif_dbg(priv, ifdown, dev, "bcmgenet_close\n"); - phy_stop(priv->phydev); + bcmgenet_netif_stop(dev); /* Disable MAC receive */ - reg = bcmgenet_umac_readl(priv, UMAC_CMD); - reg &= ~CMD_RX_EN; - bcmgenet_umac_writel(priv, reg, UMAC_CMD); - - netif_tx_stop_all_queues(dev); + umac_enable_set(priv, CMD_RX_EN, false); ret = bcmgenet_dma_teardown(priv); if (ret) return ret; /* Disable MAC transmit. TX DMA disabled have to done before this */ - reg = bcmgenet_umac_readl(priv, UMAC_CMD); - reg &= ~CMD_TX_EN; - bcmgenet_umac_writel(priv, reg, UMAC_CMD); - - napi_disable(&priv->napi); + umac_enable_set(priv, CMD_TX_EN, false); /* tx reclaim */ bcmgenet_tx_reclaim_all(dev); @@ -2157,18 +2192,9 @@ static int bcmgenet_close(struct net_device *dev) free_irq(priv->irq0, priv); free_irq(priv->irq1, priv); - /* Wait for pending work items to complete - we are stopping - * the clock now. Since interrupts are disabled, no new work - * will be scheduled. - */ - cancel_work_sync(&priv->bcmgenet_irq_work); - if (phy_is_internal(priv->phydev)) bcmgenet_power_down(priv, GENET_POWER_PASSIVE); - if (priv->wol_enabled) - clk_enable(priv->clk_wol); - if (!IS_ERR(priv->clk)) clk_disable_unprepare(priv->clk); @@ -2197,12 +2223,11 @@ static inline void bcmgenet_set_mdf_addr(struct bcmgenet_priv *priv, { u32 reg; - bcmgenet_umac_writel(priv, - addr[0] << 8 | addr[1], UMAC_MDF_ADDR + (*i * 4)); - bcmgenet_umac_writel(priv, - addr[2] << 24 | addr[3] << 16 | - addr[4] << 8 | addr[5], - UMAC_MDF_ADDR + ((*i + 1) * 4)); + bcmgenet_umac_writel(priv, addr[0] << 8 | addr[1], + UMAC_MDF_ADDR + (*i * 4)); + bcmgenet_umac_writel(priv, addr[2] << 24 | addr[3] << 16 | + addr[4] << 8 | addr[5], + UMAC_MDF_ADDR + ((*i + 1) * 4)); reg = bcmgenet_umac_readl(priv, UMAC_MDF_CTRL); reg |= (1 << (MAX_MC_COUNT - *mc)); bcmgenet_umac_writel(priv, reg, UMAC_MDF_CTRL); @@ -2219,7 +2244,7 @@ static void bcmgenet_set_rx_mode(struct net_device *dev) netif_dbg(priv, hw, dev, "%s: %08X\n", __func__, dev->flags); - /* Promiscous mode */ + /* Promiscuous mode */ reg = bcmgenet_umac_readl(priv, UMAC_CMD); if (dev->flags & IFF_PROMISC) { reg |= CMD_PROMISC; @@ -2399,7 +2424,7 @@ static void bcmgenet_set_hw_params(struct bcmgenet_priv *priv) /* Print the GENET core version */ dev_info(&priv->pdev->dev, "GENET " GENET_VER_FMT, - major, (reg >> 16) & 0x0f, reg & 0xffff); + major, (reg >> 16) & 0x0f, reg & 0xffff); #ifdef CONFIG_PHYS_ADDR_T_64BIT if (!(params->flags & GENET_HAS_40BITS)) @@ -2455,6 +2480,7 @@ static int bcmgenet_probe(struct platform_device *pdev) priv = netdev_priv(dev); priv->irq0 = platform_get_irq(pdev, 0); priv->irq1 = platform_get_irq(pdev, 1); + priv->wol_irq = platform_get_irq(pdev, 2); if (!priv->irq0 || !priv->irq1) { dev_err(&pdev->dev, "can't find IRQs\n"); err = -EINVAL; @@ -2489,6 +2515,13 @@ static int bcmgenet_probe(struct platform_device *pdev) dev->hw_features |= NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | NETIF_F_RXCSUM; + /* Request the WOL interrupt and advertise suspend if available */ + priv->wol_irq_disabled = true; + err = devm_request_irq(&pdev->dev, priv->wol_irq, bcmgenet_wol_isr, 0, + dev->name, priv); + if (!err) + device_set_wakeup_capable(&pdev->dev, 1); + /* Set the needed headroom to account for any possible * features enabling/disabling at runtime */ @@ -2566,6 +2599,111 @@ static int bcmgenet_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_PM_SLEEP +static int bcmgenet_suspend(struct device *d) +{ + struct net_device *dev = dev_get_drvdata(d); + struct bcmgenet_priv *priv = netdev_priv(dev); + int ret; + + if (!netif_running(dev)) + return 0; + + bcmgenet_netif_stop(dev); + + netif_device_detach(dev); + + /* Disable MAC receive */ + umac_enable_set(priv, CMD_RX_EN, false); + + ret = bcmgenet_dma_teardown(priv); + if (ret) + return ret; + + /* Disable MAC transmit. TX DMA disabled have to done before this */ + umac_enable_set(priv, CMD_TX_EN, false); + + /* tx reclaim */ + bcmgenet_tx_reclaim_all(dev); + bcmgenet_fini_dma(priv); + + /* Prepare the device for Wake-on-LAN and switch to the slow clock */ + if (device_may_wakeup(d) && priv->wolopts) { + bcmgenet_power_down(priv, GENET_POWER_WOL_MAGIC); + clk_prepare_enable(priv->clk_wol); + } + + /* Turn off the clocks */ + clk_disable_unprepare(priv->clk); + + return 0; +} + +static int bcmgenet_resume(struct device *d) +{ + struct net_device *dev = dev_get_drvdata(d); + struct bcmgenet_priv *priv = netdev_priv(dev); + unsigned long dma_ctrl; + int ret; + u32 reg; + + if (!netif_running(dev)) + return 0; + + /* Turn on the clock */ + ret = clk_prepare_enable(priv->clk); + if (ret) + return ret; + + bcmgenet_umac_reset(priv); + + ret = init_umac(priv); + if (ret) + goto out_clk_disable; + + if (priv->wolopts) + ret = bcmgenet_wol_resume(priv); + + if (ret) + goto out_clk_disable; + + /* disable ethernet MAC while updating its registers */ + umac_enable_set(priv, CMD_TX_EN | CMD_RX_EN, false); + + bcmgenet_set_hw_addr(priv, dev->dev_addr); + + if (phy_is_internal(priv->phydev)) { + reg = bcmgenet_ext_readl(priv, EXT_EXT_PWR_MGMT); + reg |= EXT_ENERGY_DET_MASK; + bcmgenet_ext_writel(priv, reg, EXT_EXT_PWR_MGMT); + } + + /* Disable RX/TX DMA and flush TX queues */ + dma_ctrl = bcmgenet_dma_disable(priv); + + /* Reinitialize TDMA and RDMA and SW housekeeping */ + ret = bcmgenet_init_dma(priv); + if (ret) { + netdev_err(dev, "failed to initialize DMA\n"); + goto out_clk_disable; + } + + /* Always enable ring 16 - descriptor ring */ + bcmgenet_enable_dma(priv, dma_ctrl); + + netif_device_attach(dev); + + bcmgenet_netif_start(dev); + + return 0; + +out_clk_disable: + clk_disable_unprepare(priv->clk); + return ret; +} +#endif /* CONFIG_PM_SLEEP */ + +static SIMPLE_DEV_PM_OPS(bcmgenet_pm_ops, bcmgenet_suspend, bcmgenet_resume); static struct platform_driver bcmgenet_driver = { .probe = bcmgenet_probe, @@ -2574,6 +2712,7 @@ static struct platform_driver bcmgenet_driver = { .name = "bcmgenet", .owner = THIS_MODULE, .of_match_table = bcmgenet_match, + .pm = &bcmgenet_pm_ops, }, }; module_platform_driver(bcmgenet_driver); diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.h b/drivers/net/ethernet/broadcom/genet/bcmgenet.h index e23c993b1362..c862d0666771 100644 --- a/drivers/net/ethernet/broadcom/genet/bcmgenet.h +++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.h @@ -4,18 +4,8 @@ * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License 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. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * - * -*/ + */ + #ifndef __BCMGENET_H__ #define __BCMGENET_H__ @@ -456,6 +446,7 @@ struct enet_cb { enum bcmgenet_power_mode { GENET_POWER_CABLE_SENSE = 0, GENET_POWER_PASSIVE, + GENET_POWER_WOL_MAGIC, }; struct bcmgenet_priv; @@ -513,9 +504,9 @@ struct bcmgenet_tx_ring { unsigned int cb_ptr; /* Tx ring initial CB ptr */ unsigned int end_ptr; /* Tx ring end CB ptr */ void (*int_enable)(struct bcmgenet_priv *priv, - struct bcmgenet_tx_ring *); + struct bcmgenet_tx_ring *); void (*int_disable)(struct bcmgenet_priv *priv, - struct bcmgenet_tx_ring *); + struct bcmgenet_tx_ring *); }; /* device context */ @@ -569,6 +560,8 @@ struct bcmgenet_priv { int irq1; unsigned int irq0_stat; unsigned int irq1_stat; + int wol_irq; + bool wol_irq_disabled; /* HW descriptors/checksum variables */ bool desc_64b_en; @@ -583,7 +576,6 @@ struct bcmgenet_priv { struct platform_device *pdev; /* WOL */ - unsigned long wol_enabled; struct clk *clk_wol; u32 wolopts; @@ -625,4 +617,12 @@ int bcmgenet_mii_config(struct net_device *dev); void bcmgenet_mii_exit(struct net_device *dev); void bcmgenet_mii_reset(struct net_device *dev); +/* Wake-on-LAN routines */ +void bcmgenet_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol); +int bcmgenet_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol); +int bcmgenet_wol_power_down_cfg(struct bcmgenet_priv *priv, + enum bcmgenet_power_mode mode); +void bcmgenet_wol_power_up_cfg(struct bcmgenet_priv *priv, + enum bcmgenet_power_mode mode); + #endif /* __BCMGENET_H__ */ diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet_wol.c b/drivers/net/ethernet/broadcom/genet/bcmgenet_wol.c new file mode 100644 index 000000000000..b82b7e4e06b2 --- /dev/null +++ b/drivers/net/ethernet/broadcom/genet/bcmgenet_wol.c @@ -0,0 +1,206 @@ +/* + * Broadcom GENET (Gigabit Ethernet) Wake-on-LAN support + * + * Copyright (c) 2014 Broadcom Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#define pr_fmt(fmt) "bcmgenet_wol: " fmt + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/sched.h> +#include <linux/types.h> +#include <linux/interrupt.h> +#include <linux/string.h> +#include <linux/init.h> +#include <linux/errno.h> +#include <linux/delay.h> +#include <linux/pm.h> +#include <linux/clk.h> +#include <linux/version.h> +#include <linux/platform_device.h> +#include <net/arp.h> + +#include <linux/mii.h> +#include <linux/ethtool.h> +#include <linux/netdevice.h> +#include <linux/inetdevice.h> +#include <linux/etherdevice.h> +#include <linux/skbuff.h> +#include <linux/in.h> +#include <linux/ip.h> +#include <linux/ipv6.h> +#include <linux/phy.h> + +#include "bcmgenet.h" + +/* ethtool function - get WOL (Wake on LAN) settings, Only Magic Packet + * Detection is supported through ethtool + */ +void bcmgenet_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol) +{ + struct bcmgenet_priv *priv = netdev_priv(dev); + u32 reg; + + wol->supported = WAKE_MAGIC | WAKE_MAGICSECURE; + wol->wolopts = priv->wolopts; + memset(wol->sopass, 0, sizeof(wol->sopass)); + + if (wol->wolopts & WAKE_MAGICSECURE) { + reg = bcmgenet_umac_readl(priv, UMAC_MPD_PW_MS); + put_unaligned_be16(reg, &wol->sopass[0]); + reg = bcmgenet_umac_readl(priv, UMAC_MPD_PW_LS); + put_unaligned_be32(reg, &wol->sopass[2]); + } +} + +/* ethtool function - set WOL (Wake on LAN) settings. + * Only for magic packet detection mode. + */ +int bcmgenet_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol) +{ + struct bcmgenet_priv *priv = netdev_priv(dev); + struct device *kdev = &priv->pdev->dev; + u32 reg; + + if (!device_can_wakeup(kdev)) + return -ENOTSUPP; + + if (wol->wolopts & ~(WAKE_MAGIC | WAKE_MAGICSECURE)) + return -EINVAL; + + if (wol->wolopts & WAKE_MAGICSECURE) { + bcmgenet_umac_writel(priv, get_unaligned_be16(&wol->sopass[0]), + UMAC_MPD_PW_MS); + bcmgenet_umac_writel(priv, get_unaligned_be32(&wol->sopass[2]), + UMAC_MPD_PW_LS); + reg = bcmgenet_umac_readl(priv, UMAC_MPD_CTRL); + reg |= MPD_PW_EN; + bcmgenet_umac_writel(priv, reg, UMAC_MPD_CTRL); + } + + /* Flag the device and relevant IRQ as wakeup capable */ + if (wol->wolopts) { + device_set_wakeup_enable(kdev, 1); + enable_irq_wake(priv->wol_irq); + priv->wol_irq_disabled = false; + } else { + device_set_wakeup_enable(kdev, 0); + /* Avoid unbalanced disable_irq_wake calls */ + if (!priv->wol_irq_disabled) + disable_irq_wake(priv->wol_irq); + priv->wol_irq_disabled = true; + } + + priv->wolopts = wol->wolopts; + + return 0; +} + +static int bcmgenet_poll_wol_status(struct bcmgenet_priv *priv) +{ + struct net_device *dev = priv->dev; + int retries = 0; + + while (!(bcmgenet_rbuf_readl(priv, RBUF_STATUS) + & RBUF_STATUS_WOL)) { + retries++; + if (retries > 5) { + netdev_crit(dev, "polling wol mode timeout\n"); + return -ETIMEDOUT; + } + mdelay(1); + } + + return retries; +} + +int bcmgenet_wol_power_down_cfg(struct bcmgenet_priv *priv, + enum bcmgenet_power_mode mode) +{ + struct net_device *dev = priv->dev; + u32 cpu_mask_clear; + int retries = 0; + u32 reg; + + if (mode != GENET_POWER_WOL_MAGIC) { + netif_err(priv, wol, dev, "unsupported mode: %d\n", mode); + return -EINVAL; + } + + /* disable RX */ + reg = bcmgenet_umac_readl(priv, UMAC_CMD); + reg &= ~CMD_RX_EN; + bcmgenet_umac_writel(priv, reg, UMAC_CMD); + mdelay(10); + + reg = bcmgenet_umac_readl(priv, UMAC_MPD_CTRL); + reg |= MPD_EN; + bcmgenet_umac_writel(priv, reg, UMAC_MPD_CTRL); + + /* Do not leave UniMAC in MPD mode only */ + retries = bcmgenet_poll_wol_status(priv); + if (retries < 0) { + reg = bcmgenet_umac_readl(priv, UMAC_MPD_CTRL); + reg &= ~MPD_EN; + bcmgenet_umac_writel(priv, reg, UMAC_MPD_CTRL); + return retries; + } + + netif_dbg(priv, wol, dev, "MPD WOL-ready status set after %d msec\n", + retries); + + /* Enable CRC forward */ + reg = bcmgenet_umac_readl(priv, UMAC_CMD); + priv->crc_fwd_en = 1; + reg |= CMD_CRC_FWD; + + /* Receiver must be enabled for WOL MP detection */ + reg |= CMD_RX_EN; + bcmgenet_umac_writel(priv, reg, UMAC_CMD); + + if (priv->hw_params->flags & GENET_HAS_EXT) { + reg = bcmgenet_ext_readl(priv, EXT_EXT_PWR_MGMT); + reg &= ~EXT_ENERGY_DET_MASK; + bcmgenet_ext_writel(priv, reg, EXT_EXT_PWR_MGMT); + } + + /* Enable the MPD interrupt */ + cpu_mask_clear = UMAC_IRQ_MPD_R; + + bcmgenet_intrl2_0_writel(priv, cpu_mask_clear, INTRL2_CPU_MASK_CLEAR); + + return 0; +} + +void bcmgenet_wol_power_up_cfg(struct bcmgenet_priv *priv, + enum bcmgenet_power_mode mode) +{ + u32 cpu_mask_set; + u32 reg; + + if (mode != GENET_POWER_WOL_MAGIC) { + netif_err(priv, wol, priv->dev, "invalid mode: %d\n", mode); + return; + } + + reg = bcmgenet_umac_readl(priv, UMAC_MPD_CTRL); + reg &= ~MPD_EN; + bcmgenet_umac_writel(priv, reg, UMAC_MPD_CTRL); + + /* Disable CRC Forward */ + reg = bcmgenet_umac_readl(priv, UMAC_CMD); + reg &= ~CMD_CRC_FWD; + bcmgenet_umac_writel(priv, reg, UMAC_CMD); + priv->crc_fwd_en = 0; + + /* Stop monitoring magic packet IRQ */ + cpu_mask_set = UMAC_IRQ_MPD_R; + + /* Stop monitoring magic packet IRQ */ + bcmgenet_intrl2_0_writel(priv, cpu_mask_set, INTRL2_CPU_MASK_SET); +} diff --git a/drivers/net/ethernet/broadcom/genet/bcmmii.c b/drivers/net/ethernet/broadcom/genet/bcmmii.c index add8d8596084..18961613d385 100644 --- a/drivers/net/ethernet/broadcom/genet/bcmmii.c +++ b/drivers/net/ethernet/broadcom/genet/bcmmii.c @@ -6,15 +6,6 @@ * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License 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. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ @@ -44,15 +35,15 @@ static int bcmgenet_mii_read(struct mii_bus *bus, int phy_id, int location) u32 reg; bcmgenet_umac_writel(priv, (MDIO_RD | (phy_id << MDIO_PMD_SHIFT) | - (location << MDIO_REG_SHIFT)), UMAC_MDIO_CMD); + (location << MDIO_REG_SHIFT)), UMAC_MDIO_CMD); /* Start MDIO transaction*/ reg = bcmgenet_umac_readl(priv, UMAC_MDIO_CMD); reg |= MDIO_START_BUSY; bcmgenet_umac_writel(priv, reg, UMAC_MDIO_CMD); wait_event_timeout(priv->wq, - !(bcmgenet_umac_readl(priv, UMAC_MDIO_CMD) - & MDIO_START_BUSY), - HZ / 100); + !(bcmgenet_umac_readl(priv, UMAC_MDIO_CMD) + & MDIO_START_BUSY), + HZ / 100); ret = bcmgenet_umac_readl(priv, UMAC_MDIO_CMD); if (ret & MDIO_READ_FAIL) @@ -63,22 +54,22 @@ static int bcmgenet_mii_read(struct mii_bus *bus, int phy_id, int location) /* write a value to the MII */ static int bcmgenet_mii_write(struct mii_bus *bus, int phy_id, - int location, u16 val) + int location, u16 val) { struct net_device *dev = bus->priv; struct bcmgenet_priv *priv = netdev_priv(dev); u32 reg; bcmgenet_umac_writel(priv, (MDIO_WR | (phy_id << MDIO_PMD_SHIFT) | - (location << MDIO_REG_SHIFT) | (0xffff & val)), - UMAC_MDIO_CMD); + (location << MDIO_REG_SHIFT) | (0xffff & val)), + UMAC_MDIO_CMD); reg = bcmgenet_umac_readl(priv, UMAC_MDIO_CMD); reg |= MDIO_START_BUSY; bcmgenet_umac_writel(priv, reg, UMAC_MDIO_CMD); wait_event_timeout(priv->wq, - !(bcmgenet_umac_readl(priv, UMAC_MDIO_CMD) & - MDIO_START_BUSY), - HZ / 100); + !(bcmgenet_umac_readl(priv, UMAC_MDIO_CMD) & + MDIO_START_BUSY), + HZ / 100); return 0; } @@ -136,17 +127,18 @@ static void bcmgenet_mii_setup(struct net_device *dev) /* pause capability */ if (!phydev->pause) cmd_bits |= CMD_RX_PAUSE_IGNORE | CMD_TX_PAUSE_IGNORE; + } + if (status_changed) { reg = bcmgenet_umac_readl(priv, UMAC_CMD); reg &= ~((CMD_SPEED_MASK << CMD_SPEED_SHIFT) | CMD_HD_EN | CMD_RX_PAUSE_IGNORE | CMD_TX_PAUSE_IGNORE); reg |= cmd_bits; bcmgenet_umac_writel(priv, reg, UMAC_CMD); - } - if (status_changed) phy_print_status(phydev); + } } void bcmgenet_mii_reset(struct net_device *dev) @@ -247,7 +239,7 @@ int bcmgenet_mii_config(struct net_device *dev) phy_name = "external MII"; phydev->supported &= PHY_BASIC_FEATURES; bcmgenet_sys_writel(priv, - PORT_MODE_EXT_EPHY, SYS_PORT_CTRL); + PORT_MODE_EXT_EPHY, SYS_PORT_CTRL); break; case PHY_INTERFACE_MODE_REVMII: @@ -283,7 +275,7 @@ int bcmgenet_mii_config(struct net_device *dev) reg |= RGMII_MODE_EN | id_mode_dis; bcmgenet_ext_writel(priv, reg, EXT_RGMII_OOB_CTRL); bcmgenet_sys_writel(priv, - PORT_MODE_EXT_GPHY, SYS_PORT_CTRL); + PORT_MODE_EXT_GPHY, SYS_PORT_CTRL); break; default: dev_err(kdev, "unknown phy mode: %d\n", priv->phy_interface); @@ -362,7 +354,7 @@ static int bcmgenet_mii_probe(struct net_device *dev) priv->mii_bus->irq[phydev->addr] = PHY_POLL; pr_info("attached PHY at address %d [%s]\n", - phydev->addr, phydev->drv->name); + phydev->addr, phydev->drv->name); return 0; } @@ -387,9 +379,9 @@ static int bcmgenet_mii_alloc(struct bcmgenet_priv *priv) bus->read = bcmgenet_mii_read; bus->write = bcmgenet_mii_write; snprintf(bus->id, MII_BUS_ID_SIZE, "%s-%d", - priv->pdev->name, priv->pdev->id); + priv->pdev->name, priv->pdev->id); - bus->irq = kzalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL); + bus->irq = kcalloc(PHY_MAX_ADDR, sizeof(int), GFP_KERNEL); if (!bus->irq) { mdiobus_free(priv->mii_bus); return -ENOMEM; diff --git a/drivers/net/ethernet/broadcom/tg3.c b/drivers/net/ethernet/broadcom/tg3.c index 8afa579e7c40..a3dd5dc64f4c 100644 --- a/drivers/net/ethernet/broadcom/tg3.c +++ b/drivers/net/ethernet/broadcom/tg3.c @@ -7830,17 +7830,18 @@ static int tigon3_dma_hwbug_workaround(struct tg3_napi *tnapi, static netdev_tx_t tg3_start_xmit(struct sk_buff *, struct net_device *); -/* Use GSO to workaround a rare TSO bug that may be triggered when the - * TSO header is greater than 80 bytes. +/* Use GSO to workaround all TSO packets that meet HW bug conditions + * indicated in tg3_tx_frag_set() */ -static int tg3_tso_bug(struct tg3 *tp, struct sk_buff *skb) +static int tg3_tso_bug(struct tg3 *tp, struct tg3_napi *tnapi, + struct netdev_queue *txq, struct sk_buff *skb) { struct sk_buff *segs, *nskb; u32 frag_cnt_est = skb_shinfo(skb)->gso_segs * 3; /* Estimate the number of fragments in the worst case */ - if (unlikely(tg3_tx_avail(&tp->napi[0]) <= frag_cnt_est)) { - netif_stop_queue(tp->dev); + if (unlikely(tg3_tx_avail(tnapi) <= frag_cnt_est)) { + netif_tx_stop_queue(txq); /* netif_tx_stop_queue() must be done before checking * checking tx index in tg3_tx_avail() below, because in @@ -7848,13 +7849,14 @@ static int tg3_tso_bug(struct tg3 *tp, struct sk_buff *skb) * netif_tx_queue_stopped(). */ smp_mb(); - if (tg3_tx_avail(&tp->napi[0]) <= frag_cnt_est) + if (tg3_tx_avail(tnapi) <= frag_cnt_est) return NETDEV_TX_BUSY; - netif_wake_queue(tp->dev); + netif_tx_wake_queue(txq); } - segs = skb_gso_segment(skb, tp->dev->features & ~(NETIF_F_TSO | NETIF_F_TSO6)); + segs = skb_gso_segment(skb, tp->dev->features & + ~(NETIF_F_TSO | NETIF_F_TSO6)); if (IS_ERR(segs) || !segs) goto tg3_tso_bug_end; @@ -7930,7 +7932,7 @@ static netdev_tx_t tg3_start_xmit(struct sk_buff *skb, struct net_device *dev) if (!skb_is_gso_v6(skb)) { if (unlikely((ETH_HLEN + hdr_len) > 80) && tg3_flag(tp, TSO_BUG)) - return tg3_tso_bug(tp, skb); + return tg3_tso_bug(tp, tnapi, txq, skb); ip_csum = iph->check; ip_tot_len = iph->tot_len; @@ -8061,7 +8063,7 @@ static netdev_tx_t tg3_start_xmit(struct sk_buff *skb, struct net_device *dev) iph->tot_len = ip_tot_len; } tcph->check = tcp_csum; - return tg3_tso_bug(tp, skb); + return tg3_tso_bug(tp, tnapi, txq, skb); } /* If the workaround fails due to memory/mapping |