diff options
Diffstat (limited to 'drivers/net/dsa/bcm_sf2.c')
-rw-r--r-- | drivers/net/dsa/bcm_sf2.c | 191 |
1 files changed, 120 insertions, 71 deletions
diff --git a/drivers/net/dsa/bcm_sf2.c b/drivers/net/dsa/bcm_sf2.c index 97236cfcbae4..ac621f44237a 100644 --- a/drivers/net/dsa/bcm_sf2.c +++ b/drivers/net/dsa/bcm_sf2.c | |||
@@ -16,6 +16,7 @@ | |||
16 | #include <linux/platform_device.h> | 16 | #include <linux/platform_device.h> |
17 | #include <linux/phy.h> | 17 | #include <linux/phy.h> |
18 | #include <linux/phy_fixed.h> | 18 | #include <linux/phy_fixed.h> |
19 | #include <linux/phylink.h> | ||
19 | #include <linux/mii.h> | 20 | #include <linux/mii.h> |
20 | #include <linux/of.h> | 21 | #include <linux/of.h> |
21 | #include <linux/of_irq.h> | 22 | #include <linux/of_irq.h> |
@@ -306,7 +307,8 @@ static int bcm_sf2_sw_mdio_write(struct mii_bus *bus, int addr, int regnum, | |||
306 | 307 | ||
307 | static irqreturn_t bcm_sf2_switch_0_isr(int irq, void *dev_id) | 308 | static irqreturn_t bcm_sf2_switch_0_isr(int irq, void *dev_id) |
308 | { | 309 | { |
309 | struct bcm_sf2_priv *priv = dev_id; | 310 | struct dsa_switch *ds = dev_id; |
311 | struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds); | ||
310 | 312 | ||
311 | priv->irq0_stat = intrl2_0_readl(priv, INTRL2_CPU_STATUS) & | 313 | priv->irq0_stat = intrl2_0_readl(priv, INTRL2_CPU_STATUS) & |
312 | ~priv->irq0_mask; | 314 | ~priv->irq0_mask; |
@@ -317,16 +319,21 @@ static irqreturn_t bcm_sf2_switch_0_isr(int irq, void *dev_id) | |||
317 | 319 | ||
318 | static irqreturn_t bcm_sf2_switch_1_isr(int irq, void *dev_id) | 320 | static irqreturn_t bcm_sf2_switch_1_isr(int irq, void *dev_id) |
319 | { | 321 | { |
320 | struct bcm_sf2_priv *priv = dev_id; | 322 | struct dsa_switch *ds = dev_id; |
323 | struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds); | ||
321 | 324 | ||
322 | priv->irq1_stat = intrl2_1_readl(priv, INTRL2_CPU_STATUS) & | 325 | priv->irq1_stat = intrl2_1_readl(priv, INTRL2_CPU_STATUS) & |
323 | ~priv->irq1_mask; | 326 | ~priv->irq1_mask; |
324 | intrl2_1_writel(priv, priv->irq1_stat, INTRL2_CPU_CLEAR); | 327 | intrl2_1_writel(priv, priv->irq1_stat, INTRL2_CPU_CLEAR); |
325 | 328 | ||
326 | if (priv->irq1_stat & P_LINK_UP_IRQ(P7_IRQ_OFF)) | 329 | if (priv->irq1_stat & P_LINK_UP_IRQ(P7_IRQ_OFF)) { |
327 | priv->port_sts[7].link = 1; | 330 | priv->port_sts[7].link = true; |
328 | if (priv->irq1_stat & P_LINK_DOWN_IRQ(P7_IRQ_OFF)) | 331 | dsa_port_phylink_mac_change(ds, 7, true); |
329 | priv->port_sts[7].link = 0; | 332 | } |
333 | if (priv->irq1_stat & P_LINK_DOWN_IRQ(P7_IRQ_OFF)) { | ||
334 | priv->port_sts[7].link = false; | ||
335 | dsa_port_phylink_mac_change(ds, 7, false); | ||
336 | } | ||
330 | 337 | ||
331 | return IRQ_HANDLED; | 338 | return IRQ_HANDLED; |
332 | } | 339 | } |
@@ -473,13 +480,56 @@ static u32 bcm_sf2_sw_get_phy_flags(struct dsa_switch *ds, int port) | |||
473 | return priv->hw_params.gphy_rev; | 480 | return priv->hw_params.gphy_rev; |
474 | } | 481 | } |
475 | 482 | ||
476 | static void bcm_sf2_sw_adjust_link(struct dsa_switch *ds, int port, | 483 | static void bcm_sf2_sw_validate(struct dsa_switch *ds, int port, |
477 | struct phy_device *phydev) | 484 | unsigned long *supported, |
485 | struct phylink_link_state *state) | ||
486 | { | ||
487 | __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, }; | ||
488 | |||
489 | if (!phy_interface_mode_is_rgmii(state->interface) && | ||
490 | state->interface != PHY_INTERFACE_MODE_MII && | ||
491 | state->interface != PHY_INTERFACE_MODE_REVMII && | ||
492 | state->interface != PHY_INTERFACE_MODE_GMII && | ||
493 | state->interface != PHY_INTERFACE_MODE_INTERNAL && | ||
494 | state->interface != PHY_INTERFACE_MODE_MOCA) { | ||
495 | bitmap_zero(supported, __ETHTOOL_LINK_MODE_MASK_NBITS); | ||
496 | dev_err(ds->dev, | ||
497 | "Unsupported interface: %d\n", state->interface); | ||
498 | return; | ||
499 | } | ||
500 | |||
501 | /* Allow all the expected bits */ | ||
502 | phylink_set(mask, Autoneg); | ||
503 | phylink_set_port_modes(mask); | ||
504 | phylink_set(mask, Pause); | ||
505 | phylink_set(mask, Asym_Pause); | ||
506 | |||
507 | /* With the exclusion of MII and Reverse MII, we support Gigabit, | ||
508 | * including Half duplex | ||
509 | */ | ||
510 | if (state->interface != PHY_INTERFACE_MODE_MII && | ||
511 | state->interface != PHY_INTERFACE_MODE_REVMII) { | ||
512 | phylink_set(mask, 1000baseT_Full); | ||
513 | phylink_set(mask, 1000baseT_Half); | ||
514 | } | ||
515 | |||
516 | phylink_set(mask, 10baseT_Half); | ||
517 | phylink_set(mask, 10baseT_Full); | ||
518 | phylink_set(mask, 100baseT_Half); | ||
519 | phylink_set(mask, 100baseT_Full); | ||
520 | |||
521 | bitmap_and(supported, supported, mask, | ||
522 | __ETHTOOL_LINK_MODE_MASK_NBITS); | ||
523 | bitmap_and(state->advertising, state->advertising, mask, | ||
524 | __ETHTOOL_LINK_MODE_MASK_NBITS); | ||
525 | } | ||
526 | |||
527 | static void bcm_sf2_sw_mac_config(struct dsa_switch *ds, int port, | ||
528 | unsigned int mode, | ||
529 | const struct phylink_link_state *state) | ||
478 | { | 530 | { |
479 | struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds); | 531 | struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds); |
480 | struct ethtool_eee *p = &priv->dev->ports[port].eee; | ||
481 | u32 id_mode_dis = 0, port_mode; | 532 | u32 id_mode_dis = 0, port_mode; |
482 | const char *str = NULL; | ||
483 | u32 reg, offset; | 533 | u32 reg, offset; |
484 | 534 | ||
485 | if (priv->type == BCM7445_DEVICE_ID) | 535 | if (priv->type == BCM7445_DEVICE_ID) |
@@ -487,62 +537,48 @@ static void bcm_sf2_sw_adjust_link(struct dsa_switch *ds, int port, | |||
487 | else | 537 | else |
488 | offset = CORE_STS_OVERRIDE_GMIIP2_PORT(port); | 538 | offset = CORE_STS_OVERRIDE_GMIIP2_PORT(port); |
489 | 539 | ||
490 | switch (phydev->interface) { | 540 | switch (state->interface) { |
491 | case PHY_INTERFACE_MODE_RGMII: | 541 | case PHY_INTERFACE_MODE_RGMII: |
492 | str = "RGMII (no delay)"; | ||
493 | id_mode_dis = 1; | 542 | id_mode_dis = 1; |
543 | /* fallthrough */ | ||
494 | case PHY_INTERFACE_MODE_RGMII_TXID: | 544 | case PHY_INTERFACE_MODE_RGMII_TXID: |
495 | if (!str) | ||
496 | str = "RGMII (TX delay)"; | ||
497 | port_mode = EXT_GPHY; | 545 | port_mode = EXT_GPHY; |
498 | break; | 546 | break; |
499 | case PHY_INTERFACE_MODE_MII: | 547 | case PHY_INTERFACE_MODE_MII: |
500 | str = "MII"; | ||
501 | port_mode = EXT_EPHY; | 548 | port_mode = EXT_EPHY; |
502 | break; | 549 | break; |
503 | case PHY_INTERFACE_MODE_REVMII: | 550 | case PHY_INTERFACE_MODE_REVMII: |
504 | str = "Reverse MII"; | ||
505 | port_mode = EXT_REVMII; | 551 | port_mode = EXT_REVMII; |
506 | break; | 552 | break; |
507 | default: | 553 | default: |
508 | /* All other PHYs: internal and MoCA */ | 554 | /* all other PHYs: internal and MoCA */ |
509 | goto force_link; | ||
510 | } | ||
511 | |||
512 | /* If the link is down, just disable the interface to conserve power */ | ||
513 | if (!phydev->link) { | ||
514 | reg = reg_readl(priv, REG_RGMII_CNTRL_P(port)); | ||
515 | reg &= ~RGMII_MODE_EN; | ||
516 | reg_writel(priv, reg, REG_RGMII_CNTRL_P(port)); | ||
517 | goto force_link; | 555 | goto force_link; |
518 | } | 556 | } |
519 | 557 | ||
520 | /* Clear id_mode_dis bit, and the existing port mode, but | 558 | /* Clear id_mode_dis bit, and the existing port mode, let |
521 | * make sure we enable the RGMII block for data to pass | 559 | * RGMII_MODE_EN bet set by mac_link_{up,down} |
522 | */ | 560 | */ |
523 | reg = reg_readl(priv, REG_RGMII_CNTRL_P(port)); | 561 | reg = reg_readl(priv, REG_RGMII_CNTRL_P(port)); |
524 | reg &= ~ID_MODE_DIS; | 562 | reg &= ~ID_MODE_DIS; |
525 | reg &= ~(PORT_MODE_MASK << PORT_MODE_SHIFT); | 563 | reg &= ~(PORT_MODE_MASK << PORT_MODE_SHIFT); |
526 | reg &= ~(RX_PAUSE_EN | TX_PAUSE_EN); | 564 | reg &= ~(RX_PAUSE_EN | TX_PAUSE_EN); |
527 | 565 | ||
528 | reg |= port_mode | RGMII_MODE_EN; | 566 | reg |= port_mode; |
529 | if (id_mode_dis) | 567 | if (id_mode_dis) |
530 | reg |= ID_MODE_DIS; | 568 | reg |= ID_MODE_DIS; |
531 | 569 | ||
532 | if (phydev->pause) { | 570 | if (state->pause & MLO_PAUSE_TXRX_MASK) { |
533 | if (phydev->asym_pause) | 571 | if (state->pause & MLO_PAUSE_TX) |
534 | reg |= TX_PAUSE_EN; | 572 | reg |= TX_PAUSE_EN; |
535 | reg |= RX_PAUSE_EN; | 573 | reg |= RX_PAUSE_EN; |
536 | } | 574 | } |
537 | 575 | ||
538 | reg_writel(priv, reg, REG_RGMII_CNTRL_P(port)); | 576 | reg_writel(priv, reg, REG_RGMII_CNTRL_P(port)); |
539 | 577 | ||
540 | pr_info("Port %d configured for %s\n", port, str); | ||
541 | |||
542 | force_link: | 578 | force_link: |
543 | /* Force link settings detected from the PHY */ | 579 | /* Force link settings detected from the PHY */ |
544 | reg = SW_OVERRIDE; | 580 | reg = SW_OVERRIDE; |
545 | switch (phydev->speed) { | 581 | switch (state->speed) { |
546 | case SPEED_1000: | 582 | case SPEED_1000: |
547 | reg |= SPDSTS_1000 << SPEED_SHIFT; | 583 | reg |= SPDSTS_1000 << SPEED_SHIFT; |
548 | break; | 584 | break; |
@@ -551,33 +587,61 @@ force_link: | |||
551 | break; | 587 | break; |
552 | } | 588 | } |
553 | 589 | ||
554 | if (phydev->link) | 590 | if (state->link) |
555 | reg |= LINK_STS; | 591 | reg |= LINK_STS; |
556 | if (phydev->duplex == DUPLEX_FULL) | 592 | if (state->duplex == DUPLEX_FULL) |
557 | reg |= DUPLX_MODE; | 593 | reg |= DUPLX_MODE; |
558 | 594 | ||
559 | core_writel(priv, reg, offset); | 595 | core_writel(priv, reg, offset); |
560 | |||
561 | if (!phydev->is_pseudo_fixed_link) | ||
562 | p->eee_enabled = b53_eee_init(ds, port, phydev); | ||
563 | } | 596 | } |
564 | 597 | ||
565 | static void bcm_sf2_sw_fixed_link_update(struct dsa_switch *ds, int port, | 598 | static void bcm_sf2_sw_mac_link_set(struct dsa_switch *ds, int port, |
566 | struct fixed_phy_status *status) | 599 | phy_interface_t interface, bool link) |
567 | { | 600 | { |
568 | struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds); | 601 | struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds); |
569 | u32 duplex, pause, offset; | ||
570 | u32 reg; | 602 | u32 reg; |
571 | 603 | ||
572 | if (priv->type == BCM7445_DEVICE_ID) | 604 | if (!phy_interface_mode_is_rgmii(interface) && |
573 | offset = CORE_STS_OVERRIDE_GMIIP_PORT(port); | 605 | interface != PHY_INTERFACE_MODE_MII && |
606 | interface != PHY_INTERFACE_MODE_REVMII) | ||
607 | return; | ||
608 | |||
609 | /* If the link is down, just disable the interface to conserve power */ | ||
610 | reg = reg_readl(priv, REG_RGMII_CNTRL_P(port)); | ||
611 | if (link) | ||
612 | reg |= RGMII_MODE_EN; | ||
574 | else | 613 | else |
575 | offset = CORE_STS_OVERRIDE_GMIIP2_PORT(port); | 614 | reg &= ~RGMII_MODE_EN; |
615 | reg_writel(priv, reg, REG_RGMII_CNTRL_P(port)); | ||
616 | } | ||
617 | |||
618 | static void bcm_sf2_sw_mac_link_down(struct dsa_switch *ds, int port, | ||
619 | unsigned int mode, | ||
620 | phy_interface_t interface) | ||
621 | { | ||
622 | bcm_sf2_sw_mac_link_set(ds, port, interface, false); | ||
623 | } | ||
624 | |||
625 | static void bcm_sf2_sw_mac_link_up(struct dsa_switch *ds, int port, | ||
626 | unsigned int mode, | ||
627 | phy_interface_t interface, | ||
628 | struct phy_device *phydev) | ||
629 | { | ||
630 | struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds); | ||
631 | struct ethtool_eee *p = &priv->dev->ports[port].eee; | ||
576 | 632 | ||
577 | duplex = core_readl(priv, CORE_DUPSTS); | 633 | bcm_sf2_sw_mac_link_set(ds, port, interface, true); |
578 | pause = core_readl(priv, CORE_PAUSESTS); | ||
579 | 634 | ||
580 | status->link = 0; | 635 | if (mode == MLO_AN_PHY && phydev) |
636 | p->eee_enabled = b53_eee_init(ds, port, phydev); | ||
637 | } | ||
638 | |||
639 | static void bcm_sf2_sw_fixed_state(struct dsa_switch *ds, int port, | ||
640 | struct phylink_link_state *status) | ||
641 | { | ||
642 | struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds); | ||
643 | |||
644 | status->link = false; | ||
581 | 645 | ||
582 | /* MoCA port is special as we do not get link status from CORE_LNKSTS, | 646 | /* MoCA port is special as we do not get link status from CORE_LNKSTS, |
583 | * which means that we need to force the link at the port override | 647 | * which means that we need to force the link at the port override |
@@ -596,28 +660,10 @@ static void bcm_sf2_sw_fixed_link_update(struct dsa_switch *ds, int port, | |||
596 | */ | 660 | */ |
597 | if (!status->link) | 661 | if (!status->link) |
598 | netif_carrier_off(ds->ports[port].slave); | 662 | netif_carrier_off(ds->ports[port].slave); |
599 | status->duplex = 1; | 663 | status->duplex = DUPLEX_FULL; |
600 | } else { | 664 | } else { |
601 | status->link = 1; | 665 | status->link = true; |
602 | status->duplex = !!(duplex & (1 << port)); | ||
603 | } | ||
604 | |||
605 | reg = core_readl(priv, offset); | ||
606 | reg |= SW_OVERRIDE; | ||
607 | if (status->link) | ||
608 | reg |= LINK_STS; | ||
609 | else | ||
610 | reg &= ~LINK_STS; | ||
611 | core_writel(priv, reg, offset); | ||
612 | |||
613 | if ((pause & (1 << port)) && | ||
614 | (pause & (1 << (port + PAUSESTS_TX_PAUSE_SHIFT)))) { | ||
615 | status->asym_pause = 1; | ||
616 | status->pause = 1; | ||
617 | } | 666 | } |
618 | |||
619 | if (pause & (1 << port)) | ||
620 | status->pause = 1; | ||
621 | } | 667 | } |
622 | 668 | ||
623 | static void bcm_sf2_enable_acb(struct dsa_switch *ds) | 669 | static void bcm_sf2_enable_acb(struct dsa_switch *ds) |
@@ -861,8 +907,11 @@ static const struct dsa_switch_ops bcm_sf2_ops = { | |||
861 | .get_sset_count = b53_get_sset_count, | 907 | .get_sset_count = b53_get_sset_count, |
862 | .get_ethtool_phy_stats = b53_get_ethtool_phy_stats, | 908 | .get_ethtool_phy_stats = b53_get_ethtool_phy_stats, |
863 | .get_phy_flags = bcm_sf2_sw_get_phy_flags, | 909 | .get_phy_flags = bcm_sf2_sw_get_phy_flags, |
864 | .adjust_link = bcm_sf2_sw_adjust_link, | 910 | .phylink_validate = bcm_sf2_sw_validate, |
865 | .fixed_link_update = bcm_sf2_sw_fixed_link_update, | 911 | .phylink_mac_config = bcm_sf2_sw_mac_config, |
912 | .phylink_mac_link_down = bcm_sf2_sw_mac_link_down, | ||
913 | .phylink_mac_link_up = bcm_sf2_sw_mac_link_up, | ||
914 | .phylink_fixed_state = bcm_sf2_sw_fixed_state, | ||
866 | .suspend = bcm_sf2_sw_suspend, | 915 | .suspend = bcm_sf2_sw_suspend, |
867 | .resume = bcm_sf2_sw_resume, | 916 | .resume = bcm_sf2_sw_resume, |
868 | .get_wol = bcm_sf2_sw_get_wol, | 917 | .get_wol = bcm_sf2_sw_get_wol, |
@@ -1065,14 +1114,14 @@ static int bcm_sf2_sw_probe(struct platform_device *pdev) | |||
1065 | bcm_sf2_intr_disable(priv); | 1114 | bcm_sf2_intr_disable(priv); |
1066 | 1115 | ||
1067 | ret = devm_request_irq(&pdev->dev, priv->irq0, bcm_sf2_switch_0_isr, 0, | 1116 | ret = devm_request_irq(&pdev->dev, priv->irq0, bcm_sf2_switch_0_isr, 0, |
1068 | "switch_0", priv); | 1117 | "switch_0", ds); |
1069 | if (ret < 0) { | 1118 | if (ret < 0) { |
1070 | pr_err("failed to request switch_0 IRQ\n"); | 1119 | pr_err("failed to request switch_0 IRQ\n"); |
1071 | goto out_mdio; | 1120 | goto out_mdio; |
1072 | } | 1121 | } |
1073 | 1122 | ||
1074 | ret = devm_request_irq(&pdev->dev, priv->irq1, bcm_sf2_switch_1_isr, 0, | 1123 | ret = devm_request_irq(&pdev->dev, priv->irq1, bcm_sf2_switch_1_isr, 0, |
1075 | "switch_1", priv); | 1124 | "switch_1", ds); |
1076 | if (ret < 0) { | 1125 | if (ret < 0) { |
1077 | pr_err("failed to request switch_1 IRQ\n"); | 1126 | pr_err("failed to request switch_1 IRQ\n"); |
1078 | goto out_mdio; | 1127 | goto out_mdio; |