27.03.2015 20:15, Florian Fainelli пишет:
> I think your concerns are valid, but I don't think there is going to be
> any problem with the approach I suggested because there is a contract
> that the fixed PHYs and regular PHYs need to
Hello Florian.

As promised, today I tried to resurrect my first implementation
and do things as you suggested: install the link_update callback
for mvneta privately.
I feel SGMII setup is very common and deserves the separate API,
not the per-driver handling, but in any case, I'd like to show
the implementation first, then discuss.

Unfortunately, it didn't work quite right.
The problem is that mvneta calls phy_disconnect() on .ndo_stop
callback. After that, phy->attached_dev becomes NULL, and so the
link_update callback gets called with net_dev==NULL! And crashs.
Of course I can easily work around that, but IMHO its a bug -
the one that actually gets fixed by the patches I posted previously.
They were changing the callback to receive phy_device instead of
net_device, and so NULL will never be passed.
I am attaching the new patch so that you can take a look and decide.
If you still think its fine, even despite the NULL passing w/a, then
I'll mail it with the proper boilerplate. But if you agree that
passing NULL to link_update is a bug, then maybe you'll decide to
get the whole surgery thing. :)


-=-=-=-=-=-=-=-=-=# Don't remove this line #=-=-=-=-=-=-=-=-=-
When MDIO bus is unavailable (common setup for SGMII), the in-band
signaling must be used to correctly track link state.
This patch enables the in-band status delivery for links state changes, namely:
- link up/down
- link speed
- duplex full/half
fixed_phy link_update() callback is used to update status.

Note: SGMII setup is so common that I think it deserves a separate API
rather than the per-driver handling. There is an alternative implementation
that adds such API:
https://lkml.org/lkml/2015/3/27/346

CC: Thomas Petazzoni <thomas.petazz...@free-electrons.com>
CC: Florian Fainelli <f.faine...@gmail.com>
CC: net...@vger.kernel.org
CC: linux-kernel@vger.kernel.org

Signed-off-by: Stas Sergeev <s...@users.sourceforge.net>
---
 drivers/net/ethernet/marvell/mvneta.c |   97 +++++++++++++++++++++++++++++----
 1 file changed, 86 insertions(+), 11 deletions(-)

diff --git a/drivers/net/ethernet/marvell/mvneta.c 
b/drivers/net/ethernet/marvell/mvneta.c
index 96208f1..5b9cc0a 100644
--- a/drivers/net/ethernet/marvell/mvneta.c
+++ b/drivers/net/ethernet/marvell/mvneta.c
@@ -100,6 +100,8 @@
 #define MVNETA_TXQ_CMD                           0x2448
 #define      MVNETA_TXQ_DISABLE_SHIFT            8
 #define      MVNETA_TXQ_ENABLE_MASK              0x000000ff
+#define MVNETA_GMAC_CLOCK_DIVIDER                0x24f4
+#define      MVNETA_GMAC_1MS_CLOCK_ENABLE        BIT(31)
 #define MVNETA_ACC_MODE                          0x2500
 #define MVNETA_CPU_MAP(cpu)                      (0x2540 + ((cpu) << 2))
 #define      MVNETA_CPU_RXQ_ACCESS_ALL_MASK      0x000000ff
@@ -122,6 +124,7 @@
 #define      MVNETA_TX_INTR_MASK_ALL             (0xff << 0)
 #define      MVNETA_RX_INTR_MASK(nr_rxqs)        (((1 << nr_rxqs) - 1) << 8)
 #define      MVNETA_RX_INTR_MASK_ALL             (0xff << 8)
+#define      MVNETA_MISCINTR_INTR_MASK           BIT(31)

 #define MVNETA_INTR_OLD_CAUSE                    0x25a8
 #define MVNETA_INTR_OLD_MASK                     0x25ac
@@ -165,6 +168,7 @@
 #define      MVNETA_GMAC_MAX_RX_SIZE_MASK        0x7ffc
 #define      MVNETA_GMAC0_PORT_ENABLE            BIT(0)
 #define MVNETA_GMAC_CTRL_2                       0x2c08
+#define      MVNETA_GMAC2_INBAND_AN_ENABLE       BIT(0)
 #define      MVNETA_GMAC2_PCS_ENABLE             BIT(3)
 #define      MVNETA_GMAC2_PORT_RGMII             BIT(4)
 #define      MVNETA_GMAC2_PORT_RESET             BIT(6)
@@ -180,9 +184,11 @@
 #define MVNETA_GMAC_AUTONEG_CONFIG               0x2c0c
 #define      MVNETA_GMAC_FORCE_LINK_DOWN         BIT(0)
 #define      MVNETA_GMAC_FORCE_LINK_PASS         BIT(1)
+#define      MVNETA_GMAC_INBAND_AN_ENABLE        BIT(2)
 #define      MVNETA_GMAC_CONFIG_MII_SPEED        BIT(5)
 #define      MVNETA_GMAC_CONFIG_GMII_SPEED       BIT(6)
 #define      MVNETA_GMAC_AN_SPEED_EN             BIT(7)
+#define      MVNETA_GMAC_AN_FLOW_CTRL_EN         BIT(11)
 #define      MVNETA_GMAC_CONFIG_FULL_DUPLEX      BIT(12)
 #define      MVNETA_GMAC_AN_DUPLEX_EN            BIT(13)
 #define MVNETA_MIB_COUNTERS_BASE                 0x3080
@@ -304,6 +310,7 @@ struct mvneta_port {
        unsigned int link;
        unsigned int duplex;
        unsigned int speed;
+       int inband_status;
 };

 /* The mvneta_tx_desc and mvneta_rx_desc structures describe the
@@ -994,6 +1001,20 @@ static void mvneta_defaults_set(struct mvneta_port *pp)
        val &= ~MVNETA_PHY_POLLING_ENABLE;
        mvreg_write(pp, MVNETA_UNIT_CONTROL, val);

+       if (pp->inband_status) {
+               val = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG);
+               val &= ~(MVNETA_GMAC_FORCE_LINK_PASS |
+                        MVNETA_GMAC_FORCE_LINK_DOWN |
+                        MVNETA_GMAC_AN_FLOW_CTRL_EN);
+               val |= MVNETA_GMAC_INBAND_AN_ENABLE |
+                      MVNETA_GMAC_AN_SPEED_EN |
+                      MVNETA_GMAC_AN_DUPLEX_EN;
+               mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, val);
+               val = mvreg_read(pp, MVNETA_GMAC_CLOCK_DIVIDER);
+               val |= MVNETA_GMAC_1MS_CLOCK_ENABLE;
+               mvreg_write(pp, MVNETA_GMAC_CLOCK_DIVIDER, val);
+       }
+
        mvneta_set_ucast_table(pp, -1);
        mvneta_set_special_mcast_table(pp, -1);
        mvneta_set_other_mcast_table(pp, -1);
@@ -2043,6 +2064,29 @@ static irqreturn_t mvneta_isr(int irq, void *dev_id)
        return IRQ_HANDLED;
 }

+static int mvneta_fixed_link_update(struct net_device *dev,
+                                 struct fixed_phy_status *status)
+{
+       struct mvneta_port *pp;
+       u32 gmac_stat;
+
+       if (!dev) {
+               /* we have a bug here */
+               return -EINVAL;
+       }
+       pp = netdev_priv(dev);
+       gmac_stat = mvreg_read(pp, MVNETA_GMAC_STATUS);
+       status->link = !!(gmac_stat & MVNETA_GMAC_LINK_UP);
+       if (gmac_stat & MVNETA_GMAC_SPEED_1000)
+               status->speed = SPEED_1000;
+       else if (gmac_stat & MVNETA_GMAC_SPEED_100)
+               status->speed = SPEED_100;
+       else
+               status->speed = SPEED_10;
+       status->duplex = !!(gmac_stat & MVNETA_GMAC_FULL_DUPLEX);
+       return 0;
+}
+
 /* NAPI handler
  * Bits 0 - 7 of the causeRxTx register indicate that are transmitted
  * packets on the corresponding TXQ (Bit 0 is for TX queue 1).
@@ -2063,8 +2107,12 @@ static int mvneta_poll(struct napi_struct *napi, int 
budget)
        }

        /* Read cause register */
-       cause_rx_tx = mvreg_read(pp, MVNETA_INTR_NEW_CAUSE) &
-               (MVNETA_RX_INTR_MASK(rxq_number) | 
MVNETA_TX_INTR_MASK(txq_number));
+       cause_rx_tx = mvreg_read(pp, MVNETA_INTR_NEW_CAUSE);
+       if (cause_rx_tx & MVNETA_MISCINTR_INTR_MASK) {
+               /* since phylib uses polling, not much to do here...
+                * Any way to tell phylib to do the poll NOW? */
+               mvreg_write(pp, MVNETA_INTR_MISC_CAUSE, 0);
+       }

        /* Release Tx descriptors */
        if (cause_rx_tx & MVNETA_TX_INTR_MASK_ALL) {
@@ -2109,7 +2157,9 @@ static int mvneta_poll(struct napi_struct *napi, int 
budget)
                napi_complete(napi);
                local_irq_save(flags);
                mvreg_write(pp, MVNETA_INTR_NEW_MASK,
-                           MVNETA_RX_INTR_MASK(rxq_number) | 
MVNETA_TX_INTR_MASK(txq_number));
+                           MVNETA_RX_INTR_MASK(rxq_number) |
+                           MVNETA_TX_INTR_MASK(txq_number) |
+                           MVNETA_MISCINTR_INTR_MASK);
                local_irq_restore(flags);
        }

@@ -2373,7 +2423,13 @@ static void mvneta_start_dev(struct mvneta_port *pp)

        /* Unmask interrupts */
        mvreg_write(pp, MVNETA_INTR_NEW_MASK,
-                   MVNETA_RX_INTR_MASK(rxq_number) | 
MVNETA_TX_INTR_MASK(txq_number));
+                   MVNETA_RX_INTR_MASK(rxq_number) |
+                   MVNETA_TX_INTR_MASK(txq_number) |
+                   MVNETA_MISCINTR_INTR_MASK);
+       mvreg_write(pp, MVNETA_INTR_MISC_MASK,
+                   MVNETA_CAUSE_PHY_STATUS_CHANGE |
+                   MVNETA_CAUSE_LINK_CHANGE |
+                   MVNETA_CAUSE_PSC_SYNC_CHANGE);

        phy_start(pp->phy_dev);
        netif_tx_start_all_queues(pp->dev);
@@ -2523,9 +2579,7 @@ static void mvneta_adjust_link(struct net_device *ndev)
                        val = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG);
                        val &= ~(MVNETA_GMAC_CONFIG_MII_SPEED |
                                 MVNETA_GMAC_CONFIG_GMII_SPEED |
-                                MVNETA_GMAC_CONFIG_FULL_DUPLEX |
-                                MVNETA_GMAC_AN_SPEED_EN |
-                                MVNETA_GMAC_AN_DUPLEX_EN);
+                                MVNETA_GMAC_CONFIG_FULL_DUPLEX);

                        if (phydev->duplex)
                                val |= MVNETA_GMAC_CONFIG_FULL_DUPLEX;
@@ -2554,12 +2608,24 @@ static void mvneta_adjust_link(struct net_device *ndev)

        if (status_change) {
                if (phydev->link) {
-                       u32 val = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG);
-                       val |= (MVNETA_GMAC_FORCE_LINK_PASS |
-                               MVNETA_GMAC_FORCE_LINK_DOWN);
-                       mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, val);
+                       if (!pp->inband_status) {
+                               u32 val = mvreg_read(pp,
+                                                 MVNETA_GMAC_AUTONEG_CONFIG);
+                               val &= ~MVNETA_GMAC_FORCE_LINK_DOWN;
+                               val |= MVNETA_GMAC_FORCE_LINK_PASS;
+                               mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG,
+                                           val);
+                       }
                        mvneta_port_up(pp);
                } else {
+                       if (!pp->inband_status) {
+                               u32 val = mvreg_read(pp,
+                                                 MVNETA_GMAC_AUTONEG_CONFIG);
+                               val &= ~MVNETA_GMAC_FORCE_LINK_PASS;
+                               val |= MVNETA_GMAC_FORCE_LINK_DOWN;
+                               mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG,
+                                           val);
+                       }
                        mvneta_port_down(pp);
                }
                phy_print_status(phydev);
@@ -2910,6 +2976,9 @@ static int mvneta_port_power_up(struct mvneta_port *pp, 
int phy_mode)
                return -EINVAL;
        }

+       if (pp->inband_status)
+               ctrl |= MVNETA_GMAC2_INBAND_AN_ENABLE;
+
        /* Cancel Port Reset */
        ctrl &= ~MVNETA_GMAC2_PORT_RESET;
        mvreg_write(pp, MVNETA_GMAC_CTRL_2, ctrl);
@@ -2934,6 +3003,7 @@ static int mvneta_probe(struct platform_device *pdev)
        char hw_mac_addr[ETH_ALEN];
        const char *mac_from;
        int phy_mode;
+       int fixed_phy = 0;
        int err;

        /* Our multiqueue support is not complete, so for now, only
@@ -2956,6 +3026,7 @@ static int mvneta_probe(struct platform_device *pdev)

        phy_node = of_parse_phandle(dn, "phy", 0);
        if (!phy_node) {
+               struct phy_device *phy;
                if (!of_phy_is_fixed_link(dn)) {
                        dev_err(&pdev->dev, "no PHY specified\n");
                        err = -ENODEV;
@@ -2967,11 +3038,14 @@ static int mvneta_probe(struct platform_device *pdev)
                        dev_err(&pdev->dev, "cannot register fixed PHY\n");
                        goto err_free_irq;
                }
+               fixed_phy = 1;

                /* In the case of a fixed PHY, the DT node associated
                 * to the PHY is the Ethernet MAC DT node.
                 */
                phy_node = of_node_get(dn);
+               phy = of_phy_find_device(dn);
+               fixed_phy_set_link_update(phy, mvneta_fixed_link_update);
        }

        phy_mode = of_get_phy_mode(dn);
@@ -2990,6 +3064,7 @@ static int mvneta_probe(struct platform_device *pdev)
        pp = netdev_priv(dev);
        pp->phy_node = phy_node;
        pp->phy_interface = phy_mode;
+       pp->inband_status = (phy_mode == PHY_INTERFACE_MODE_SGMII) && fixed_phy;

        pp->clk = devm_clk_get(&pdev->dev, NULL);
        if (IS_ERR(pp->clk)) {
-- 
1.7.9.5
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to