net-next: stmmac: mediatek: add more support for RMII
authorBiao Huang <biao.huang@mediatek.com>
Mon, 16 Dec 2019 05:39:57 +0000 (13:39 +0800)
committerDavid S. Miller <davem@davemloft.net>
Tue, 17 Dec 2019 21:48:30 +0000 (13:48 -0800)
MT2712 SoC can provide the rmii reference clock, and the clock
will output from TXC pin only, which means ref_clk pin of external
PHY should connect to TXC pin in this case.
Add corresponding clock and timing settings.

Signed-off-by: Biao Huang <biao.huang@mediatek.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/stmicro/stmmac/dwmac-mediatek.c

index bdb8042..9e4b838 100644 (file)
@@ -55,6 +55,8 @@ struct mediatek_dwmac_plat_data {
        struct regmap *peri_regmap;
        struct device *dev;
        phy_interface_t phy_mode;
+       int num_clks_to_config;
+       bool rmii_clk_from_mac;
        bool rmii_rxc;
 };
 
@@ -73,21 +75,33 @@ struct mediatek_dwmac_variant {
 
 /* list of clocks required for mac */
 static const char * const mt2712_dwmac_clk_l[] = {
-       "axi", "apb", "mac_main", "ptp_ref"
+       "axi", "apb", "mac_main", "ptp_ref", "rmii_internal"
 };
 
 static int mt2712_set_interface(struct mediatek_dwmac_plat_data *plat)
 {
+       int rmii_clk_from_mac = plat->rmii_clk_from_mac ? RMII_CLK_SRC_INTERNAL : 0;
        int rmii_rxc = plat->rmii_rxc ? RMII_CLK_SRC_RXC : 0;
        u32 intf_val = 0;
 
+       /* The clock labeled as "rmii_internal" in mt2712_dwmac_clk_l is needed
+        * only in RMII(when MAC provides the reference clock), and useless for
+        * RGMII/MII/RMII(when PHY provides the reference clock).
+        * num_clks_to_config indicates the real number of clocks should be
+        * configured, equals to (plat->variant->num_clks - 1) in default for all the case,
+        * then +1 for rmii_clk_from_mac case.
+        */
+       plat->num_clks_to_config = plat->variant->num_clks - 1;
+
        /* select phy interface in top control domain */
        switch (plat->phy_mode) {
        case PHY_INTERFACE_MODE_MII:
                intf_val |= PHY_INTF_MII;
                break;
        case PHY_INTERFACE_MODE_RMII:
-               intf_val |= (PHY_INTF_RMII | rmii_rxc);
+               if (plat->rmii_clk_from_mac)
+                       plat->num_clks_to_config++;
+               intf_val |= (PHY_INTF_RMII | rmii_rxc | rmii_clk_from_mac);
                break;
        case PHY_INTERFACE_MODE_RGMII:
        case PHY_INTERFACE_MODE_RGMII_TXID:
@@ -173,35 +187,50 @@ static int mt2712_set_delay(struct mediatek_dwmac_plat_data *plat)
                delay_val |= FIELD_PREP(ETH_DLY_RXC_INV, mac_delay->rx_inv);
                break;
        case PHY_INTERFACE_MODE_RMII:
-               /* the rmii reference clock is from external phy,
-                * and the property "rmii_rxc" indicates which pin(TXC/RXC)
-                * the reference clk is connected to. The reference clock is a
-                * received signal, so rx_delay/rx_inv are used to indicate
-                * the reference clock timing adjustment
-                */
-               if (plat->rmii_rxc) {
-                       /* the rmii reference clock from outside is connected
-                        * to RXC pin, the reference clock will be adjusted
-                        * by RXC delay macro circuit.
-                        */
-                       delay_val |= FIELD_PREP(ETH_DLY_RXC_ENABLE, !!mac_delay->rx_delay);
-                       delay_val |= FIELD_PREP(ETH_DLY_RXC_STAGES, mac_delay->rx_delay);
-                       delay_val |= FIELD_PREP(ETH_DLY_RXC_INV, mac_delay->rx_inv);
-               } else {
-                       /* the rmii reference clock from outside is connected
-                        * to TXC pin, the reference clock will be adjusted
-                        * by TXC delay macro circuit.
+               if (plat->rmii_clk_from_mac) {
+                       /* case 1: mac provides the rmii reference clock,
+                        * and the clock output to TXC pin.
+                        * The egress timing can be adjusted by GTXC delay macro circuit.
+                        * The ingress timing can be adjusted by TXC delay macro circuit.
                         */
                        delay_val |= FIELD_PREP(ETH_DLY_TXC_ENABLE, !!mac_delay->rx_delay);
                        delay_val |= FIELD_PREP(ETH_DLY_TXC_STAGES, mac_delay->rx_delay);
                        delay_val |= FIELD_PREP(ETH_DLY_TXC_INV, mac_delay->rx_inv);
+
+                       delay_val |= FIELD_PREP(ETH_DLY_GTXC_ENABLE, !!mac_delay->tx_delay);
+                       delay_val |= FIELD_PREP(ETH_DLY_GTXC_STAGES, mac_delay->tx_delay);
+                       delay_val |= FIELD_PREP(ETH_DLY_GTXC_INV, mac_delay->tx_inv);
+               } else {
+                       /* case 2: the rmii reference clock is from external phy,
+                        * and the property "rmii_rxc" indicates which pin(TXC/RXC)
+                        * the reference clk is connected to. The reference clock is a
+                        * received signal, so rx_delay/rx_inv are used to indicate
+                        * the reference clock timing adjustment
+                        */
+                       if (plat->rmii_rxc) {
+                               /* the rmii reference clock from outside is connected
+                                * to RXC pin, the reference clock will be adjusted
+                                * by RXC delay macro circuit.
+                                */
+                               delay_val |= FIELD_PREP(ETH_DLY_RXC_ENABLE, !!mac_delay->rx_delay);
+                               delay_val |= FIELD_PREP(ETH_DLY_RXC_STAGES, mac_delay->rx_delay);
+                               delay_val |= FIELD_PREP(ETH_DLY_RXC_INV, mac_delay->rx_inv);
+                       } else {
+                               /* the rmii reference clock from outside is connected
+                                * to TXC pin, the reference clock will be adjusted
+                                * by TXC delay macro circuit.
+                                */
+                               delay_val |= FIELD_PREP(ETH_DLY_TXC_ENABLE, !!mac_delay->rx_delay);
+                               delay_val |= FIELD_PREP(ETH_DLY_TXC_STAGES, mac_delay->rx_delay);
+                               delay_val |= FIELD_PREP(ETH_DLY_TXC_INV, mac_delay->rx_inv);
+                       }
+                       /* tx_inv will inverse the tx clock inside mac relateive to
+                        * reference clock from external phy,
+                        * and this bit is located in the same register with fine-tune
+                        */
+                       if (mac_delay->tx_inv)
+                               fine_val = ETH_RMII_DLY_TX_INV;
                }
-               /* tx_inv will inverse the tx clock inside mac relateive to
-                * reference clock from external phy,
-                * and this bit is located in the same register with fine-tune
-                */
-               if (mac_delay->tx_inv)
-                       fine_val = ETH_RMII_DLY_TX_INV;
                break;
        case PHY_INTERFACE_MODE_RGMII:
        case PHY_INTERFACE_MODE_RGMII_TXID:
@@ -278,6 +307,7 @@ static int mediatek_dwmac_config_dt(struct mediatek_dwmac_plat_data *plat)
        mac_delay->tx_inv = of_property_read_bool(plat->np, "mediatek,txc-inverse");
        mac_delay->rx_inv = of_property_read_bool(plat->np, "mediatek,rxc-inverse");
        plat->rmii_rxc = of_property_read_bool(plat->np, "mediatek,rmii-rxc");
+       plat->rmii_clk_from_mac = of_property_read_bool(plat->np, "mediatek,rmii-clk-from-mac");
 
        return 0;
 }
@@ -294,6 +324,8 @@ static int mediatek_dwmac_clk_init(struct mediatek_dwmac_plat_data *plat)
        for (i = 0; i < num; i++)
                plat->clks[i].id = variant->clk_list[i];
 
+       plat->num_clks_to_config = variant->num_clks;
+
        return devm_clk_bulk_get(plat->dev, num, plat->clks);
 }
 
@@ -321,7 +353,7 @@ static int mediatek_dwmac_init(struct platform_device *pdev, void *priv)
                return ret;
        }
 
-       ret = clk_bulk_prepare_enable(variant->num_clks, plat->clks);
+       ret = clk_bulk_prepare_enable(plat->num_clks_to_config, plat->clks);
        if (ret) {
                dev_err(plat->dev, "failed to enable clks, err = %d\n", ret);
                return ret;
@@ -336,9 +368,8 @@ static int mediatek_dwmac_init(struct platform_device *pdev, void *priv)
 static void mediatek_dwmac_exit(struct platform_device *pdev, void *priv)
 {
        struct mediatek_dwmac_plat_data *plat = priv;
-       const struct mediatek_dwmac_variant *variant = plat->variant;
 
-       clk_bulk_disable_unprepare(variant->num_clks, plat->clks);
+       clk_bulk_disable_unprepare(plat->num_clks_to_config, plat->clks);
 
        pm_runtime_put_sync(&pdev->dev);
        pm_runtime_disable(&pdev->dev);