diff options
author | David S. Miller <davem@davemloft.net> | 2014-07-07 23:56:55 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2014-07-07 23:56:55 -0400 |
commit | 081a20ffccd24d2f5fd06debcff57697fa2ff069 (patch) | |
tree | 5b594a1a1946c8c42609ae8ded841067bf94dd68 | |
parent | 4710d806fcb825156e0a7b3a81104915c5e90f5d (diff) | |
parent | 83e82f4c706bbca3e2d9d7962e63605cc7a5fbd8 (diff) |
Merge branch 'systemport-next'
Florian Fainelli says:
====================
net: systemport: PM and Wake-on-LAN support
This patchset brings Power Management and Wake-on-LAN support to the
Broadcom SYSTEM PORT driver.
S2 and S3 modes are supported, while we only support Wake-on-LAN using
MagicPackets for now
====================
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | Documentation/devicetree/bindings/net/broadcom-systemport.txt | 3 | ||||
-rw-r--r-- | drivers/net/ethernet/broadcom/bcmsysport.c | 378 | ||||
-rw-r--r-- | drivers/net/ethernet/broadcom/bcmsysport.h | 14 |
3 files changed, 361 insertions, 34 deletions
diff --git a/Documentation/devicetree/bindings/net/broadcom-systemport.txt b/Documentation/devicetree/bindings/net/broadcom-systemport.txt index c183ea90d9bc..aa7ad622259d 100644 --- a/Documentation/devicetree/bindings/net/broadcom-systemport.txt +++ b/Documentation/devicetree/bindings/net/broadcom-systemport.txt | |||
@@ -4,7 +4,8 @@ Required properties: | |||
4 | - compatible: should be one of "brcm,systemport-v1.00" or "brcm,systemport" | 4 | - compatible: should be one of "brcm,systemport-v1.00" or "brcm,systemport" |
5 | - reg: address and length of the register set for the device. | 5 | - reg: address and length of the register set for the device. |
6 | - interrupts: interrupts for the device, first cell must be for the the rx | 6 | - interrupts: interrupts for the device, first cell must be for the the rx |
7 | interrupts, and the second cell should be for the transmit queues | 7 | interrupts, and the second cell should be for the transmit queues. An |
8 | optional third interrupt cell for Wake-on-LAN can be specified | ||
8 | - local-mac-address: Ethernet MAC address (48 bits) of this adapter | 9 | - local-mac-address: Ethernet MAC address (48 bits) of this adapter |
9 | - phy-mode: Should be a string describing the PHY interface to the | 10 | - phy-mode: Should be a string describing the PHY interface to the |
10 | Ethernet switch/PHY, see Documentation/devicetree/bindings/net/ethernet.txt | 11 | Ethernet switch/PHY, see Documentation/devicetree/bindings/net/ethernet.txt |
diff --git a/drivers/net/ethernet/broadcom/bcmsysport.c b/drivers/net/ethernet/broadcom/bcmsysport.c index 141160ef249a..7a1bd2b3bc26 100644 --- a/drivers/net/ethernet/broadcom/bcmsysport.c +++ b/drivers/net/ethernet/broadcom/bcmsysport.c | |||
@@ -124,9 +124,9 @@ static int bcm_sysport_set_rx_csum(struct net_device *dev, | |||
124 | struct bcm_sysport_priv *priv = netdev_priv(dev); | 124 | struct bcm_sysport_priv *priv = netdev_priv(dev); |
125 | u32 reg; | 125 | u32 reg; |
126 | 126 | ||
127 | priv->rx_csum_en = !!(wanted & NETIF_F_RXCSUM); | 127 | priv->rx_chk_en = !!(wanted & NETIF_F_RXCSUM); |
128 | reg = rxchk_readl(priv, RXCHK_CONTROL); | 128 | reg = rxchk_readl(priv, RXCHK_CONTROL); |
129 | if (priv->rx_csum_en) | 129 | if (priv->rx_chk_en) |
130 | reg |= RXCHK_EN; | 130 | reg |= RXCHK_EN; |
131 | else | 131 | else |
132 | reg &= ~RXCHK_EN; | 132 | reg &= ~RXCHK_EN; |
@@ -134,7 +134,7 @@ static int bcm_sysport_set_rx_csum(struct net_device *dev, | |||
134 | /* If UniMAC forwards CRC, we need to skip over it to get | 134 | /* If UniMAC forwards CRC, we need to skip over it to get |
135 | * a valid CHK bit to be set in the per-packet status word | 135 | * a valid CHK bit to be set in the per-packet status word |
136 | */ | 136 | */ |
137 | if (priv->rx_csum_en && priv->crc_fwd) | 137 | if (priv->rx_chk_en && priv->crc_fwd) |
138 | reg |= RXCHK_SKIP_FCS; | 138 | reg |= RXCHK_SKIP_FCS; |
139 | else | 139 | else |
140 | reg &= ~RXCHK_SKIP_FCS; | 140 | reg &= ~RXCHK_SKIP_FCS; |
@@ -384,6 +384,64 @@ static void bcm_sysport_get_stats(struct net_device *dev, | |||
384 | } | 384 | } |
385 | } | 385 | } |
386 | 386 | ||
387 | static void bcm_sysport_get_wol(struct net_device *dev, | ||
388 | struct ethtool_wolinfo *wol) | ||
389 | { | ||
390 | struct bcm_sysport_priv *priv = netdev_priv(dev); | ||
391 | u32 reg; | ||
392 | |||
393 | wol->supported = WAKE_MAGIC | WAKE_MAGICSECURE; | ||
394 | wol->wolopts = priv->wolopts; | ||
395 | |||
396 | if (!(priv->wolopts & WAKE_MAGICSECURE)) | ||
397 | return; | ||
398 | |||
399 | /* Return the programmed SecureOn password */ | ||
400 | reg = umac_readl(priv, UMAC_PSW_MS); | ||
401 | put_unaligned_be16(reg, &wol->sopass[0]); | ||
402 | reg = umac_readl(priv, UMAC_PSW_LS); | ||
403 | put_unaligned_be32(reg, &wol->sopass[2]); | ||
404 | } | ||
405 | |||
406 | static int bcm_sysport_set_wol(struct net_device *dev, | ||
407 | struct ethtool_wolinfo *wol) | ||
408 | { | ||
409 | struct bcm_sysport_priv *priv = netdev_priv(dev); | ||
410 | struct device *kdev = &priv->pdev->dev; | ||
411 | u32 supported = WAKE_MAGIC | WAKE_MAGICSECURE; | ||
412 | |||
413 | if (!device_can_wakeup(kdev)) | ||
414 | return -ENOTSUPP; | ||
415 | |||
416 | if (wol->wolopts & ~supported) | ||
417 | return -EINVAL; | ||
418 | |||
419 | /* Program the SecureOn password */ | ||
420 | if (wol->wolopts & WAKE_MAGICSECURE) { | ||
421 | umac_writel(priv, get_unaligned_be16(&wol->sopass[0]), | ||
422 | UMAC_PSW_MS); | ||
423 | umac_writel(priv, get_unaligned_be32(&wol->sopass[2]), | ||
424 | UMAC_PSW_LS); | ||
425 | } | ||
426 | |||
427 | /* Flag the device and relevant IRQ as wakeup capable */ | ||
428 | if (wol->wolopts) { | ||
429 | device_set_wakeup_enable(kdev, 1); | ||
430 | enable_irq_wake(priv->wol_irq); | ||
431 | priv->wol_irq_disabled = 0; | ||
432 | } else { | ||
433 | device_set_wakeup_enable(kdev, 0); | ||
434 | /* Avoid unbalanced disable_irq_wake calls */ | ||
435 | if (!priv->wol_irq_disabled) | ||
436 | disable_irq_wake(priv->wol_irq); | ||
437 | priv->wol_irq_disabled = 1; | ||
438 | } | ||
439 | |||
440 | priv->wolopts = wol->wolopts; | ||
441 | |||
442 | return 0; | ||
443 | } | ||
444 | |||
387 | static void bcm_sysport_free_cb(struct bcm_sysport_cb *cb) | 445 | static void bcm_sysport_free_cb(struct bcm_sysport_cb *cb) |
388 | { | 446 | { |
389 | dev_kfree_skb_any(cb->skb); | 447 | dev_kfree_skb_any(cb->skb); |
@@ -692,6 +750,20 @@ static int bcm_sysport_poll(struct napi_struct *napi, int budget) | |||
692 | return work_done; | 750 | return work_done; |
693 | } | 751 | } |
694 | 752 | ||
753 | static void bcm_sysport_resume_from_wol(struct bcm_sysport_priv *priv) | ||
754 | { | ||
755 | u32 reg; | ||
756 | |||
757 | /* Stop monitoring MPD interrupt */ | ||
758 | intrl2_0_mask_set(priv, INTRL2_0_MPD); | ||
759 | |||
760 | /* Clear the MagicPacket detection logic */ | ||
761 | reg = umac_readl(priv, UMAC_MPD_CTRL); | ||
762 | reg &= ~MPD_EN; | ||
763 | umac_writel(priv, reg, UMAC_MPD_CTRL); | ||
764 | |||
765 | netif_dbg(priv, wol, priv->netdev, "resumed from WOL\n"); | ||
766 | } | ||
695 | 767 | ||
696 | /* RX and misc interrupt routine */ | 768 | /* RX and misc interrupt routine */ |
697 | static irqreturn_t bcm_sysport_rx_isr(int irq, void *dev_id) | 769 | static irqreturn_t bcm_sysport_rx_isr(int irq, void *dev_id) |
@@ -722,6 +794,11 @@ static irqreturn_t bcm_sysport_rx_isr(int irq, void *dev_id) | |||
722 | if (priv->irq0_stat & INTRL2_0_TX_RING_FULL) | 794 | if (priv->irq0_stat & INTRL2_0_TX_RING_FULL) |
723 | bcm_sysport_tx_reclaim_all(priv); | 795 | bcm_sysport_tx_reclaim_all(priv); |
724 | 796 | ||
797 | if (priv->irq0_stat & INTRL2_0_MPD) { | ||
798 | netdev_info(priv->netdev, "Wake-on-LAN interrupt!\n"); | ||
799 | bcm_sysport_resume_from_wol(priv); | ||
800 | } | ||
801 | |||
725 | return IRQ_HANDLED; | 802 | return IRQ_HANDLED; |
726 | } | 803 | } |
727 | 804 | ||
@@ -757,6 +834,15 @@ static irqreturn_t bcm_sysport_tx_isr(int irq, void *dev_id) | |||
757 | return IRQ_HANDLED; | 834 | return IRQ_HANDLED; |
758 | } | 835 | } |
759 | 836 | ||
837 | static irqreturn_t bcm_sysport_wol_isr(int irq, void *dev_id) | ||
838 | { | ||
839 | struct bcm_sysport_priv *priv = dev_id; | ||
840 | |||
841 | pm_wakeup_event(&priv->pdev->dev, 0); | ||
842 | |||
843 | return IRQ_HANDLED; | ||
844 | } | ||
845 | |||
760 | static int bcm_sysport_insert_tsb(struct sk_buff *skb, struct net_device *dev) | 846 | static int bcm_sysport_insert_tsb(struct sk_buff *skb, struct net_device *dev) |
761 | { | 847 | { |
762 | struct sk_buff *nskb; | 848 | struct sk_buff *nskb; |
@@ -1236,15 +1322,15 @@ static void bcm_sysport_set_rx_mode(struct net_device *dev) | |||
1236 | } | 1322 | } |
1237 | 1323 | ||
1238 | static inline void umac_enable_set(struct bcm_sysport_priv *priv, | 1324 | static inline void umac_enable_set(struct bcm_sysport_priv *priv, |
1239 | unsigned int enable) | 1325 | u32 mask, unsigned int enable) |
1240 | { | 1326 | { |
1241 | u32 reg; | 1327 | u32 reg; |
1242 | 1328 | ||
1243 | reg = umac_readl(priv, UMAC_CMD); | 1329 | reg = umac_readl(priv, UMAC_CMD); |
1244 | if (enable) | 1330 | if (enable) |
1245 | reg |= CMD_RX_EN | CMD_TX_EN; | 1331 | reg |= mask; |
1246 | else | 1332 | else |
1247 | reg &= ~(CMD_RX_EN | CMD_TX_EN); | 1333 | reg &= ~mask; |
1248 | umac_writel(priv, reg, UMAC_CMD); | 1334 | umac_writel(priv, reg, UMAC_CMD); |
1249 | 1335 | ||
1250 | /* UniMAC stops on a packet boundary, wait for a full-sized packet | 1336 | /* UniMAC stops on a packet boundary, wait for a full-sized packet |
@@ -1295,11 +1381,35 @@ static void topctrl_flush(struct bcm_sysport_priv *priv) | |||
1295 | topctrl_writel(priv, 0, TX_FLUSH_CNTL); | 1381 | topctrl_writel(priv, 0, TX_FLUSH_CNTL); |
1296 | } | 1382 | } |
1297 | 1383 | ||
1384 | static void bcm_sysport_netif_start(struct net_device *dev) | ||
1385 | { | ||
1386 | struct bcm_sysport_priv *priv = netdev_priv(dev); | ||
1387 | |||
1388 | /* Enable NAPI */ | ||
1389 | napi_enable(&priv->napi); | ||
1390 | |||
1391 | phy_start(priv->phydev); | ||
1392 | |||
1393 | /* Enable TX interrupts for the 32 TXQs */ | ||
1394 | intrl2_1_mask_clear(priv, 0xffffffff); | ||
1395 | |||
1396 | /* Last call before we start the real business */ | ||
1397 | netif_tx_start_all_queues(dev); | ||
1398 | } | ||
1399 | |||
1400 | static void rbuf_init(struct bcm_sysport_priv *priv) | ||
1401 | { | ||
1402 | u32 reg; | ||
1403 | |||
1404 | reg = rbuf_readl(priv, RBUF_CONTROL); | ||
1405 | reg |= RBUF_4B_ALGN | RBUF_RSB_EN; | ||
1406 | rbuf_writel(priv, reg, RBUF_CONTROL); | ||
1407 | } | ||
1408 | |||
1298 | static int bcm_sysport_open(struct net_device *dev) | 1409 | static int bcm_sysport_open(struct net_device *dev) |
1299 | { | 1410 | { |
1300 | struct bcm_sysport_priv *priv = netdev_priv(dev); | 1411 | struct bcm_sysport_priv *priv = netdev_priv(dev); |
1301 | unsigned int i; | 1412 | unsigned int i; |
1302 | u32 reg; | ||
1303 | int ret; | 1413 | int ret; |
1304 | 1414 | ||
1305 | /* Reset UniMAC */ | 1415 | /* Reset UniMAC */ |
@@ -1313,12 +1423,10 @@ static int bcm_sysport_open(struct net_device *dev) | |||
1313 | topctrl_flush(priv); | 1423 | topctrl_flush(priv); |
1314 | 1424 | ||
1315 | /* Disable the UniMAC RX/TX */ | 1425 | /* Disable the UniMAC RX/TX */ |
1316 | umac_enable_set(priv, 0); | 1426 | umac_enable_set(priv, CMD_RX_EN | CMD_TX_EN, 0); |
1317 | 1427 | ||
1318 | /* Enable RBUF 2bytes alignment and Receive Status Block */ | 1428 | /* Enable RBUF 2bytes alignment and Receive Status Block */ |
1319 | reg = rbuf_readl(priv, RBUF_CONTROL); | 1429 | rbuf_init(priv); |
1320 | reg |= RBUF_4B_ALGN | RBUF_RSB_EN; | ||
1321 | rbuf_writel(priv, reg, RBUF_CONTROL); | ||
1322 | 1430 | ||
1323 | /* Set maximum frame length */ | 1431 | /* Set maximum frame length */ |
1324 | umac_writel(priv, UMAC_MAX_MTU_SIZE, UMAC_MAX_FRAME_LEN); | 1432 | umac_writel(priv, UMAC_MAX_MTU_SIZE, UMAC_MAX_FRAME_LEN); |
@@ -1394,19 +1502,10 @@ static int bcm_sysport_open(struct net_device *dev) | |||
1394 | if (ret) | 1502 | if (ret) |
1395 | goto out_clear_rx_int; | 1503 | goto out_clear_rx_int; |
1396 | 1504 | ||
1397 | /* Enable NAPI */ | ||
1398 | napi_enable(&priv->napi); | ||
1399 | |||
1400 | /* Turn on UniMAC TX/RX */ | 1505 | /* Turn on UniMAC TX/RX */ |
1401 | umac_enable_set(priv, 1); | 1506 | umac_enable_set(priv, CMD_RX_EN | CMD_TX_EN, 1); |
1402 | |||
1403 | phy_start(priv->phydev); | ||
1404 | 1507 | ||
1405 | /* Enable TX interrupts for the 32 TXQs */ | 1508 | bcm_sysport_netif_start(dev); |
1406 | intrl2_1_mask_clear(priv, 0xffffffff); | ||
1407 | |||
1408 | /* Last call before we start the real business */ | ||
1409 | netif_tx_start_all_queues(dev); | ||
1410 | 1509 | ||
1411 | return 0; | 1510 | return 0; |
1412 | 1511 | ||
@@ -1425,12 +1524,9 @@ out_phy_disconnect: | |||
1425 | return ret; | 1524 | return ret; |
1426 | } | 1525 | } |
1427 | 1526 | ||
1428 | static int bcm_sysport_stop(struct net_device *dev) | 1527 | static void bcm_sysport_netif_stop(struct net_device *dev) |
1429 | { | 1528 | { |
1430 | struct bcm_sysport_priv *priv = netdev_priv(dev); | 1529 | struct bcm_sysport_priv *priv = netdev_priv(dev); |
1431 | unsigned int i; | ||
1432 | u32 reg; | ||
1433 | int ret; | ||
1434 | 1530 | ||
1435 | /* stop all software from updating hardware */ | 1531 | /* stop all software from updating hardware */ |
1436 | netif_tx_stop_all_queues(dev); | 1532 | netif_tx_stop_all_queues(dev); |
@@ -1442,11 +1538,18 @@ static int bcm_sysport_stop(struct net_device *dev) | |||
1442 | intrl2_0_writel(priv, 0xffffffff, INTRL2_CPU_CLEAR); | 1538 | intrl2_0_writel(priv, 0xffffffff, INTRL2_CPU_CLEAR); |
1443 | intrl2_1_mask_set(priv, 0xffffffff); | 1539 | intrl2_1_mask_set(priv, 0xffffffff); |
1444 | intrl2_1_writel(priv, 0xffffffff, INTRL2_CPU_CLEAR); | 1540 | intrl2_1_writel(priv, 0xffffffff, INTRL2_CPU_CLEAR); |
1541 | } | ||
1542 | |||
1543 | static int bcm_sysport_stop(struct net_device *dev) | ||
1544 | { | ||
1545 | struct bcm_sysport_priv *priv = netdev_priv(dev); | ||
1546 | unsigned int i; | ||
1547 | int ret; | ||
1548 | |||
1549 | bcm_sysport_netif_stop(dev); | ||
1445 | 1550 | ||
1446 | /* Disable UniMAC RX */ | 1551 | /* Disable UniMAC RX */ |
1447 | reg = umac_readl(priv, UMAC_CMD); | 1552 | umac_enable_set(priv, CMD_RX_EN, 0); |
1448 | reg &= ~CMD_RX_EN; | ||
1449 | umac_writel(priv, reg, UMAC_CMD); | ||
1450 | 1553 | ||
1451 | ret = tdma_enable_set(priv, 0); | 1554 | ret = tdma_enable_set(priv, 0); |
1452 | if (ret) { | 1555 | if (ret) { |
@@ -1464,9 +1567,7 @@ static int bcm_sysport_stop(struct net_device *dev) | |||
1464 | } | 1567 | } |
1465 | 1568 | ||
1466 | /* Disable UniMAC TX */ | 1569 | /* Disable UniMAC TX */ |
1467 | reg = umac_readl(priv, UMAC_CMD); | 1570 | umac_enable_set(priv, CMD_TX_EN, 0); |
1468 | reg &= ~CMD_TX_EN; | ||
1469 | umac_writel(priv, reg, UMAC_CMD); | ||
1470 | 1571 | ||
1471 | /* Free RX/TX rings SW structures */ | 1572 | /* Free RX/TX rings SW structures */ |
1472 | for (i = 0; i < dev->num_tx_queues; i++) | 1573 | for (i = 0; i < dev->num_tx_queues; i++) |
@@ -1492,6 +1593,8 @@ static struct ethtool_ops bcm_sysport_ethtool_ops = { | |||
1492 | .get_strings = bcm_sysport_get_strings, | 1593 | .get_strings = bcm_sysport_get_strings, |
1493 | .get_ethtool_stats = bcm_sysport_get_stats, | 1594 | .get_ethtool_stats = bcm_sysport_get_stats, |
1494 | .get_sset_count = bcm_sysport_get_sset_count, | 1595 | .get_sset_count = bcm_sysport_get_sset_count, |
1596 | .get_wol = bcm_sysport_get_wol, | ||
1597 | .set_wol = bcm_sysport_set_wol, | ||
1495 | }; | 1598 | }; |
1496 | 1599 | ||
1497 | static const struct net_device_ops bcm_sysport_netdev_ops = { | 1600 | static const struct net_device_ops bcm_sysport_netdev_ops = { |
@@ -1533,6 +1636,7 @@ static int bcm_sysport_probe(struct platform_device *pdev) | |||
1533 | 1636 | ||
1534 | priv->irq0 = platform_get_irq(pdev, 0); | 1637 | priv->irq0 = platform_get_irq(pdev, 0); |
1535 | priv->irq1 = platform_get_irq(pdev, 1); | 1638 | priv->irq1 = platform_get_irq(pdev, 1); |
1639 | priv->wol_irq = platform_get_irq(pdev, 2); | ||
1536 | if (priv->irq0 <= 0 || priv->irq1 <= 0) { | 1640 | if (priv->irq0 <= 0 || priv->irq1 <= 0) { |
1537 | dev_err(&pdev->dev, "invalid interrupts\n"); | 1641 | dev_err(&pdev->dev, "invalid interrupts\n"); |
1538 | ret = -EINVAL; | 1642 | ret = -EINVAL; |
@@ -1585,6 +1689,13 @@ static int bcm_sysport_probe(struct platform_device *pdev) | |||
1585 | dev->hw_features |= NETIF_F_RXCSUM | NETIF_F_HIGHDMA | | 1689 | dev->hw_features |= NETIF_F_RXCSUM | NETIF_F_HIGHDMA | |
1586 | NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM; | 1690 | NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM; |
1587 | 1691 | ||
1692 | /* Request the WOL interrupt and advertise suspend if available */ | ||
1693 | priv->wol_irq_disabled = 1; | ||
1694 | ret = devm_request_irq(&pdev->dev, priv->wol_irq, | ||
1695 | bcm_sysport_wol_isr, 0, dev->name, priv); | ||
1696 | if (!ret) | ||
1697 | device_set_wakeup_capable(&pdev->dev, 1); | ||
1698 | |||
1588 | /* Set the needed headroom once and for all */ | 1699 | /* Set the needed headroom once and for all */ |
1589 | BUILD_BUG_ON(sizeof(struct bcm_tsb) != 8); | 1700 | BUILD_BUG_ON(sizeof(struct bcm_tsb) != 8); |
1590 | dev->needed_headroom += sizeof(struct bcm_tsb); | 1701 | dev->needed_headroom += sizeof(struct bcm_tsb); |
@@ -1631,6 +1742,208 @@ static int bcm_sysport_remove(struct platform_device *pdev) | |||
1631 | return 0; | 1742 | return 0; |
1632 | } | 1743 | } |
1633 | 1744 | ||
1745 | #ifdef CONFIG_PM_SLEEP | ||
1746 | static int bcm_sysport_suspend_to_wol(struct bcm_sysport_priv *priv) | ||
1747 | { | ||
1748 | struct net_device *ndev = priv->netdev; | ||
1749 | unsigned int timeout = 1000; | ||
1750 | u32 reg; | ||
1751 | |||
1752 | /* Password has already been programmed */ | ||
1753 | reg = umac_readl(priv, UMAC_MPD_CTRL); | ||
1754 | reg |= MPD_EN; | ||
1755 | reg &= ~PSW_EN; | ||
1756 | if (priv->wolopts & WAKE_MAGICSECURE) | ||
1757 | reg |= PSW_EN; | ||
1758 | umac_writel(priv, reg, UMAC_MPD_CTRL); | ||
1759 | |||
1760 | /* Make sure RBUF entered WoL mode as result */ | ||
1761 | do { | ||
1762 | reg = rbuf_readl(priv, RBUF_STATUS); | ||
1763 | if (reg & RBUF_WOL_MODE) | ||
1764 | break; | ||
1765 | |||
1766 | udelay(10); | ||
1767 | } while (timeout-- > 0); | ||
1768 | |||
1769 | /* Do not leave the UniMAC RBUF matching only MPD packets */ | ||
1770 | if (!timeout) { | ||
1771 | reg = umac_readl(priv, UMAC_MPD_CTRL); | ||
1772 | reg &= ~MPD_EN; | ||
1773 | umac_writel(priv, reg, UMAC_MPD_CTRL); | ||
1774 | netif_err(priv, wol, ndev, "failed to enter WOL mode\n"); | ||
1775 | return -ETIMEDOUT; | ||
1776 | } | ||
1777 | |||
1778 | /* UniMAC receive needs to be turned on */ | ||
1779 | umac_enable_set(priv, CMD_RX_EN, 1); | ||
1780 | |||
1781 | /* Enable the interrupt wake-up source */ | ||
1782 | intrl2_0_mask_clear(priv, INTRL2_0_MPD); | ||
1783 | |||
1784 | netif_dbg(priv, wol, ndev, "entered WOL mode\n"); | ||
1785 | |||
1786 | return 0; | ||
1787 | } | ||
1788 | |||
1789 | static int bcm_sysport_suspend(struct device *d) | ||
1790 | { | ||
1791 | struct net_device *dev = dev_get_drvdata(d); | ||
1792 | struct bcm_sysport_priv *priv = netdev_priv(dev); | ||
1793 | unsigned int i; | ||
1794 | int ret = 0; | ||
1795 | u32 reg; | ||
1796 | |||
1797 | if (!netif_running(dev)) | ||
1798 | return 0; | ||
1799 | |||
1800 | bcm_sysport_netif_stop(dev); | ||
1801 | |||
1802 | phy_suspend(priv->phydev); | ||
1803 | |||
1804 | netif_device_detach(dev); | ||
1805 | |||
1806 | /* Disable UniMAC RX */ | ||
1807 | umac_enable_set(priv, CMD_RX_EN, 0); | ||
1808 | |||
1809 | ret = rdma_enable_set(priv, 0); | ||
1810 | if (ret) { | ||
1811 | netdev_err(dev, "RDMA timeout!\n"); | ||
1812 | return ret; | ||
1813 | } | ||
1814 | |||
1815 | /* Disable RXCHK if enabled */ | ||
1816 | if (priv->rx_chk_en) { | ||
1817 | reg = rxchk_readl(priv, RXCHK_CONTROL); | ||
1818 | reg &= ~RXCHK_EN; | ||
1819 | rxchk_writel(priv, reg, RXCHK_CONTROL); | ||
1820 | } | ||
1821 | |||
1822 | /* Flush RX pipe */ | ||
1823 | if (!priv->wolopts) | ||
1824 | topctrl_writel(priv, RX_FLUSH, RX_FLUSH_CNTL); | ||
1825 | |||
1826 | ret = tdma_enable_set(priv, 0); | ||
1827 | if (ret) { | ||
1828 | netdev_err(dev, "TDMA timeout!\n"); | ||
1829 | return ret; | ||
1830 | } | ||
1831 | |||
1832 | /* Wait for a packet boundary */ | ||
1833 | usleep_range(2000, 3000); | ||
1834 | |||
1835 | umac_enable_set(priv, CMD_TX_EN, 0); | ||
1836 | |||
1837 | topctrl_writel(priv, TX_FLUSH, TX_FLUSH_CNTL); | ||
1838 | |||
1839 | /* Free RX/TX rings SW structures */ | ||
1840 | for (i = 0; i < dev->num_tx_queues; i++) | ||
1841 | bcm_sysport_fini_tx_ring(priv, i); | ||
1842 | bcm_sysport_fini_rx_ring(priv); | ||
1843 | |||
1844 | /* Get prepared for Wake-on-LAN */ | ||
1845 | if (device_may_wakeup(d) && priv->wolopts) | ||
1846 | ret = bcm_sysport_suspend_to_wol(priv); | ||
1847 | |||
1848 | return ret; | ||
1849 | } | ||
1850 | |||
1851 | static int bcm_sysport_resume(struct device *d) | ||
1852 | { | ||
1853 | struct net_device *dev = dev_get_drvdata(d); | ||
1854 | struct bcm_sysport_priv *priv = netdev_priv(dev); | ||
1855 | unsigned int i; | ||
1856 | u32 reg; | ||
1857 | int ret; | ||
1858 | |||
1859 | if (!netif_running(dev)) | ||
1860 | return 0; | ||
1861 | |||
1862 | /* We may have been suspended and never received a WOL event that | ||
1863 | * would turn off MPD detection, take care of that now | ||
1864 | */ | ||
1865 | bcm_sysport_resume_from_wol(priv); | ||
1866 | |||
1867 | /* Initialize both hardware and software ring */ | ||
1868 | for (i = 0; i < dev->num_tx_queues; i++) { | ||
1869 | ret = bcm_sysport_init_tx_ring(priv, i); | ||
1870 | if (ret) { | ||
1871 | netdev_err(dev, "failed to initialize TX ring %d\n", | ||
1872 | i); | ||
1873 | goto out_free_tx_rings; | ||
1874 | } | ||
1875 | } | ||
1876 | |||
1877 | /* Initialize linked-list */ | ||
1878 | tdma_writel(priv, TDMA_LL_RAM_INIT_BUSY, TDMA_STATUS); | ||
1879 | |||
1880 | /* Initialize RX ring */ | ||
1881 | ret = bcm_sysport_init_rx_ring(priv); | ||
1882 | if (ret) { | ||
1883 | netdev_err(dev, "failed to initialize RX ring\n"); | ||
1884 | goto out_free_rx_ring; | ||
1885 | } | ||
1886 | |||
1887 | netif_device_attach(dev); | ||
1888 | |||
1889 | /* Enable RX interrupt and TX ring full interrupt */ | ||
1890 | intrl2_0_mask_clear(priv, INTRL2_0_RDMA_MBDONE | INTRL2_0_TX_RING_FULL); | ||
1891 | |||
1892 | /* RX pipe enable */ | ||
1893 | topctrl_writel(priv, 0, RX_FLUSH_CNTL); | ||
1894 | |||
1895 | ret = rdma_enable_set(priv, 1); | ||
1896 | if (ret) { | ||
1897 | netdev_err(dev, "failed to enable RDMA\n"); | ||
1898 | goto out_free_rx_ring; | ||
1899 | } | ||
1900 | |||
1901 | /* Enable rxhck */ | ||
1902 | if (priv->rx_chk_en) { | ||
1903 | reg = rxchk_readl(priv, RXCHK_CONTROL); | ||
1904 | reg |= RXCHK_EN; | ||
1905 | rxchk_writel(priv, reg, RXCHK_CONTROL); | ||
1906 | } | ||
1907 | |||
1908 | rbuf_init(priv); | ||
1909 | |||
1910 | /* Set maximum frame length */ | ||
1911 | umac_writel(priv, UMAC_MAX_MTU_SIZE, UMAC_MAX_FRAME_LEN); | ||
1912 | |||
1913 | /* Set MAC address */ | ||
1914 | umac_set_hw_addr(priv, dev->dev_addr); | ||
1915 | |||
1916 | umac_enable_set(priv, CMD_RX_EN, 1); | ||
1917 | |||
1918 | /* TX pipe enable */ | ||
1919 | topctrl_writel(priv, 0, TX_FLUSH_CNTL); | ||
1920 | |||
1921 | umac_enable_set(priv, CMD_TX_EN, 1); | ||
1922 | |||
1923 | ret = tdma_enable_set(priv, 1); | ||
1924 | if (ret) { | ||
1925 | netdev_err(dev, "TDMA timeout!\n"); | ||
1926 | goto out_free_rx_ring; | ||
1927 | } | ||
1928 | |||
1929 | phy_resume(priv->phydev); | ||
1930 | |||
1931 | bcm_sysport_netif_start(dev); | ||
1932 | |||
1933 | return 0; | ||
1934 | |||
1935 | out_free_rx_ring: | ||
1936 | bcm_sysport_fini_rx_ring(priv); | ||
1937 | out_free_tx_rings: | ||
1938 | for (i = 0; i < dev->num_tx_queues; i++) | ||
1939 | bcm_sysport_fini_tx_ring(priv, i); | ||
1940 | return ret; | ||
1941 | } | ||
1942 | #endif | ||
1943 | |||
1944 | static SIMPLE_DEV_PM_OPS(bcm_sysport_pm_ops, | ||
1945 | bcm_sysport_suspend, bcm_sysport_resume); | ||
1946 | |||
1634 | static const struct of_device_id bcm_sysport_of_match[] = { | 1947 | static const struct of_device_id bcm_sysport_of_match[] = { |
1635 | { .compatible = "brcm,systemport-v1.00" }, | 1948 | { .compatible = "brcm,systemport-v1.00" }, |
1636 | { .compatible = "brcm,systemport" }, | 1949 | { .compatible = "brcm,systemport" }, |
@@ -1644,6 +1957,7 @@ static struct platform_driver bcm_sysport_driver = { | |||
1644 | .name = "brcm-systemport", | 1957 | .name = "brcm-systemport", |
1645 | .owner = THIS_MODULE, | 1958 | .owner = THIS_MODULE, |
1646 | .of_match_table = bcm_sysport_of_match, | 1959 | .of_match_table = bcm_sysport_of_match, |
1960 | .pm = &bcm_sysport_pm_ops, | ||
1647 | }, | 1961 | }, |
1648 | }; | 1962 | }; |
1649 | module_platform_driver(bcm_sysport_driver); | 1963 | 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 { | |||
246 | #define MIB_RX_CNT_RST (1 << 0) | 246 | #define MIB_RX_CNT_RST (1 << 0) |
247 | #define MIB_RUNT_CNT_RST (1 << 1) | 247 | #define MIB_RUNT_CNT_RST (1 << 1) |
248 | #define MIB_TX_CNT_RST (1 << 2) | 248 | #define MIB_TX_CNT_RST (1 << 2) |
249 | |||
250 | #define UMAC_MPD_CTRL 0x620 | ||
251 | #define MPD_EN (1 << 0) | ||
252 | #define MSEQ_LEN_SHIFT 16 | ||
253 | #define MSEQ_LEN_MASK 0xff | ||
254 | #define PSW_EN (1 << 27) | ||
255 | |||
256 | #define UMAC_PSW_MS 0x624 | ||
257 | #define UMAC_PSW_LS 0x628 | ||
249 | #define UMAC_MDF_CTRL 0x650 | 258 | #define UMAC_MDF_CTRL 0x650 |
250 | #define UMAC_MDF_ADDR 0x654 | 259 | #define UMAC_MDF_ADDR 0x654 |
251 | 260 | ||
@@ -642,6 +651,7 @@ struct bcm_sysport_priv { | |||
642 | struct platform_device *pdev; | 651 | struct platform_device *pdev; |
643 | int irq0; | 652 | int irq0; |
644 | int irq1; | 653 | int irq1; |
654 | int wol_irq; | ||
645 | 655 | ||
646 | /* Transmit rings */ | 656 | /* Transmit rings */ |
647 | struct bcm_sysport_tx_ring tx_rings[TDMA_NUM_RINGS]; | 657 | struct bcm_sysport_tx_ring tx_rings[TDMA_NUM_RINGS]; |
@@ -664,10 +674,12 @@ struct bcm_sysport_priv { | |||
664 | int old_duplex; | 674 | int old_duplex; |
665 | 675 | ||
666 | /* Misc fields */ | 676 | /* Misc fields */ |
667 | unsigned int rx_csum_en:1; | 677 | unsigned int rx_chk_en:1; |
668 | unsigned int tsb_en:1; | 678 | unsigned int tsb_en:1; |
669 | unsigned int crc_fwd:1; | 679 | unsigned int crc_fwd:1; |
670 | u16 rev; | 680 | u16 rev; |
681 | u32 wolopts; | ||
682 | unsigned int wol_irq_disabled:1; | ||
671 | 683 | ||
672 | /* MIB related fields */ | 684 | /* MIB related fields */ |
673 | struct bcm_sysport_mib mib; | 685 | struct bcm_sysport_mib mib; |