Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 // Copyright (c) 2018-2019 MediaTek Inc.
0003 
0004 /* A library for MediaTek SGMII circuit
0005  *
0006  * Author: Sean Wang <sean.wang@mediatek.com>
0007  *
0008  */
0009 
0010 #include <linux/mfd/syscon.h>
0011 #include <linux/of.h>
0012 #include <linux/phylink.h>
0013 #include <linux/regmap.h>
0014 
0015 #include "mtk_eth_soc.h"
0016 
0017 static struct mtk_pcs *pcs_to_mtk_pcs(struct phylink_pcs *pcs)
0018 {
0019     return container_of(pcs, struct mtk_pcs, pcs);
0020 }
0021 
0022 /* For SGMII interface mode */
0023 static int mtk_pcs_setup_mode_an(struct mtk_pcs *mpcs)
0024 {
0025     unsigned int val;
0026 
0027     /* Setup the link timer and QPHY power up inside SGMIISYS */
0028     regmap_write(mpcs->regmap, SGMSYS_PCS_LINK_TIMER,
0029              SGMII_LINK_TIMER_DEFAULT);
0030 
0031     regmap_read(mpcs->regmap, SGMSYS_SGMII_MODE, &val);
0032     val |= SGMII_REMOTE_FAULT_DIS;
0033     regmap_write(mpcs->regmap, SGMSYS_SGMII_MODE, val);
0034 
0035     regmap_read(mpcs->regmap, SGMSYS_PCS_CONTROL_1, &val);
0036     val |= SGMII_AN_RESTART;
0037     regmap_write(mpcs->regmap, SGMSYS_PCS_CONTROL_1, val);
0038 
0039     regmap_read(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, &val);
0040     val &= ~SGMII_PHYA_PWD;
0041     regmap_write(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, val);
0042 
0043     return 0;
0044 
0045 }
0046 
0047 /* For 1000BASE-X and 2500BASE-X interface modes, which operate at a
0048  * fixed speed.
0049  */
0050 static int mtk_pcs_setup_mode_force(struct mtk_pcs *mpcs,
0051                     phy_interface_t interface)
0052 {
0053     unsigned int val;
0054 
0055     regmap_read(mpcs->regmap, mpcs->ana_rgc3, &val);
0056     val &= ~RG_PHY_SPEED_MASK;
0057     if (interface == PHY_INTERFACE_MODE_2500BASEX)
0058         val |= RG_PHY_SPEED_3_125G;
0059     regmap_write(mpcs->regmap, mpcs->ana_rgc3, val);
0060 
0061     /* Disable SGMII AN */
0062     regmap_read(mpcs->regmap, SGMSYS_PCS_CONTROL_1, &val);
0063     val &= ~SGMII_AN_ENABLE;
0064     regmap_write(mpcs->regmap, SGMSYS_PCS_CONTROL_1, val);
0065 
0066     /* Set the speed etc but leave the duplex unchanged */
0067     regmap_read(mpcs->regmap, SGMSYS_SGMII_MODE, &val);
0068     val &= SGMII_DUPLEX_FULL | ~SGMII_IF_MODE_MASK;
0069     val |= SGMII_SPEED_1000;
0070     regmap_write(mpcs->regmap, SGMSYS_SGMII_MODE, val);
0071 
0072     /* Release PHYA power down state */
0073     regmap_read(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, &val);
0074     val &= ~SGMII_PHYA_PWD;
0075     regmap_write(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, val);
0076 
0077     return 0;
0078 }
0079 
0080 static int mtk_pcs_config(struct phylink_pcs *pcs, unsigned int mode,
0081               phy_interface_t interface,
0082               const unsigned long *advertising,
0083               bool permit_pause_to_mac)
0084 {
0085     struct mtk_pcs *mpcs = pcs_to_mtk_pcs(pcs);
0086     int err = 0;
0087 
0088     /* Setup SGMIISYS with the determined property */
0089     if (interface != PHY_INTERFACE_MODE_SGMII)
0090         err = mtk_pcs_setup_mode_force(mpcs, interface);
0091     else if (phylink_autoneg_inband(mode))
0092         err = mtk_pcs_setup_mode_an(mpcs);
0093 
0094     return err;
0095 }
0096 
0097 static void mtk_pcs_restart_an(struct phylink_pcs *pcs)
0098 {
0099     struct mtk_pcs *mpcs = pcs_to_mtk_pcs(pcs);
0100     unsigned int val;
0101 
0102     regmap_read(mpcs->regmap, SGMSYS_PCS_CONTROL_1, &val);
0103     val |= SGMII_AN_RESTART;
0104     regmap_write(mpcs->regmap, SGMSYS_PCS_CONTROL_1, val);
0105 }
0106 
0107 static void mtk_pcs_link_up(struct phylink_pcs *pcs, unsigned int mode,
0108                 phy_interface_t interface, int speed, int duplex)
0109 {
0110     struct mtk_pcs *mpcs = pcs_to_mtk_pcs(pcs);
0111     unsigned int val;
0112 
0113     if (!phy_interface_mode_is_8023z(interface))
0114         return;
0115 
0116     /* SGMII force duplex setting */
0117     regmap_read(mpcs->regmap, SGMSYS_SGMII_MODE, &val);
0118     val &= ~SGMII_DUPLEX_FULL;
0119     if (duplex == DUPLEX_FULL)
0120         val |= SGMII_DUPLEX_FULL;
0121 
0122     regmap_write(mpcs->regmap, SGMSYS_SGMII_MODE, val);
0123 }
0124 
0125 static const struct phylink_pcs_ops mtk_pcs_ops = {
0126     .pcs_config = mtk_pcs_config,
0127     .pcs_an_restart = mtk_pcs_restart_an,
0128     .pcs_link_up = mtk_pcs_link_up,
0129 };
0130 
0131 int mtk_sgmii_init(struct mtk_sgmii *ss, struct device_node *r, u32 ana_rgc3)
0132 {
0133     struct device_node *np;
0134     int i;
0135 
0136     for (i = 0; i < MTK_MAX_DEVS; i++) {
0137         np = of_parse_phandle(r, "mediatek,sgmiisys", i);
0138         if (!np)
0139             break;
0140 
0141         ss->pcs[i].ana_rgc3 = ana_rgc3;
0142         ss->pcs[i].regmap = syscon_node_to_regmap(np);
0143         of_node_put(np);
0144         if (IS_ERR(ss->pcs[i].regmap))
0145             return PTR_ERR(ss->pcs[i].regmap);
0146 
0147         ss->pcs[i].pcs.ops = &mtk_pcs_ops;
0148     }
0149 
0150     return 0;
0151 }
0152 
0153 struct phylink_pcs *mtk_sgmii_select_pcs(struct mtk_sgmii *ss, int id)
0154 {
0155     if (!ss->pcs[id].regmap)
0156         return NULL;
0157 
0158     return &ss->pcs[id].pcs;
0159 }