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; |
