1 // SPDX-License-Identifier: GPL-2.0
2 // Copyright (c) 2018-2019 MediaTek Inc.
3
4 /* A library for MediaTek SGMII circuit
5 *
6 * Author: Sean Wang <sean.wang@mediatek.com>
7 *
8 */
9
10 #include <linux/mfd/syscon.h>
11 #include <linux/of.h>
12 #include <linux/phylink.h>
13 #include <linux/regmap.h>
14
15 #include "mtk_eth_soc.h"
16
pcs_to_mtk_pcs(struct phylink_pcs * pcs)17 static struct mtk_pcs *pcs_to_mtk_pcs(struct phylink_pcs *pcs)
18 {
19 return container_of(pcs, struct mtk_pcs, pcs);
20 }
21
22 /* For SGMII interface mode */
mtk_pcs_setup_mode_an(struct mtk_pcs * mpcs)23 static int mtk_pcs_setup_mode_an(struct mtk_pcs *mpcs)
24 {
25 unsigned int val;
26
27 /* Setup the link timer and QPHY power up inside SGMIISYS */
28 regmap_write(mpcs->regmap, SGMSYS_PCS_LINK_TIMER,
29 SGMII_LINK_TIMER_DEFAULT);
30
31 regmap_read(mpcs->regmap, SGMSYS_SGMII_MODE, &val);
32 val |= SGMII_REMOTE_FAULT_DIS;
33 regmap_write(mpcs->regmap, SGMSYS_SGMII_MODE, val);
34
35 regmap_read(mpcs->regmap, SGMSYS_PCS_CONTROL_1, &val);
36 val |= SGMII_AN_RESTART;
37 regmap_write(mpcs->regmap, SGMSYS_PCS_CONTROL_1, val);
38
39 regmap_read(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, &val);
40 val &= ~SGMII_PHYA_PWD;
41 regmap_write(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, val);
42
43 return 0;
44
45 }
46
47 /* For 1000BASE-X and 2500BASE-X interface modes, which operate at a
48 * fixed speed.
49 */
mtk_pcs_setup_mode_force(struct mtk_pcs * mpcs,phy_interface_t interface)50 static int mtk_pcs_setup_mode_force(struct mtk_pcs *mpcs,
51 phy_interface_t interface)
52 {
53 unsigned int val;
54
55 regmap_read(mpcs->regmap, mpcs->ana_rgc3, &val);
56 val &= ~RG_PHY_SPEED_MASK;
57 if (interface == PHY_INTERFACE_MODE_2500BASEX)
58 val |= RG_PHY_SPEED_3_125G;
59 regmap_write(mpcs->regmap, mpcs->ana_rgc3, val);
60
61 /* Disable SGMII AN */
62 regmap_read(mpcs->regmap, SGMSYS_PCS_CONTROL_1, &val);
63 val &= ~SGMII_AN_ENABLE;
64 regmap_write(mpcs->regmap, SGMSYS_PCS_CONTROL_1, val);
65
66 /* Set the speed etc but leave the duplex unchanged */
67 regmap_read(mpcs->regmap, SGMSYS_SGMII_MODE, &val);
68 val &= SGMII_DUPLEX_FULL | ~SGMII_IF_MODE_MASK;
69 val |= SGMII_SPEED_1000;
70 regmap_write(mpcs->regmap, SGMSYS_SGMII_MODE, val);
71
72 /* Release PHYA power down state */
73 regmap_read(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, &val);
74 val &= ~SGMII_PHYA_PWD;
75 regmap_write(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, val);
76
77 return 0;
78 }
79
mtk_pcs_config(struct phylink_pcs * pcs,unsigned int mode,phy_interface_t interface,const unsigned long * advertising,bool permit_pause_to_mac)80 static int mtk_pcs_config(struct phylink_pcs *pcs, unsigned int mode,
81 phy_interface_t interface,
82 const unsigned long *advertising,
83 bool permit_pause_to_mac)
84 {
85 struct mtk_pcs *mpcs = pcs_to_mtk_pcs(pcs);
86 int err = 0;
87
88 /* Setup SGMIISYS with the determined property */
89 if (interface != PHY_INTERFACE_MODE_SGMII)
90 err = mtk_pcs_setup_mode_force(mpcs, interface);
91 else if (phylink_autoneg_inband(mode))
92 err = mtk_pcs_setup_mode_an(mpcs);
93
94 return err;
95 }
96
mtk_pcs_restart_an(struct phylink_pcs * pcs)97 static void mtk_pcs_restart_an(struct phylink_pcs *pcs)
98 {
99 struct mtk_pcs *mpcs = pcs_to_mtk_pcs(pcs);
100 unsigned int val;
101
102 regmap_read(mpcs->regmap, SGMSYS_PCS_CONTROL_1, &val);
103 val |= SGMII_AN_RESTART;
104 regmap_write(mpcs->regmap, SGMSYS_PCS_CONTROL_1, val);
105 }
106
mtk_pcs_link_up(struct phylink_pcs * pcs,unsigned int mode,phy_interface_t interface,int speed,int duplex)107 static void mtk_pcs_link_up(struct phylink_pcs *pcs, unsigned int mode,
108 phy_interface_t interface, int speed, int duplex)
109 {
110 struct mtk_pcs *mpcs = pcs_to_mtk_pcs(pcs);
111 unsigned int val;
112
113 if (!phy_interface_mode_is_8023z(interface))
114 return;
115
116 /* SGMII force duplex setting */
117 regmap_read(mpcs->regmap, SGMSYS_SGMII_MODE, &val);
118 val &= ~SGMII_DUPLEX_FULL;
119 if (duplex == DUPLEX_FULL)
120 val |= SGMII_DUPLEX_FULL;
121
122 regmap_write(mpcs->regmap, SGMSYS_SGMII_MODE, val);
123 }
124
125 static const struct phylink_pcs_ops mtk_pcs_ops = {
126 .pcs_config = mtk_pcs_config,
127 .pcs_an_restart = mtk_pcs_restart_an,
128 .pcs_link_up = mtk_pcs_link_up,
129 };
130
mtk_sgmii_init(struct mtk_sgmii * ss,struct device_node * r,u32 ana_rgc3)131 int mtk_sgmii_init(struct mtk_sgmii *ss, struct device_node *r, u32 ana_rgc3)
132 {
133 struct device_node *np;
134 int i;
135
136 for (i = 0; i < MTK_MAX_DEVS; i++) {
137 np = of_parse_phandle(r, "mediatek,sgmiisys", i);
138 if (!np)
139 break;
140
141 ss->pcs[i].ana_rgc3 = ana_rgc3;
142 ss->pcs[i].regmap = syscon_node_to_regmap(np);
143 of_node_put(np);
144 if (IS_ERR(ss->pcs[i].regmap))
145 return PTR_ERR(ss->pcs[i].regmap);
146
147 ss->pcs[i].pcs.ops = &mtk_pcs_ops;
148 }
149
150 return 0;
151 }
152
mtk_sgmii_select_pcs(struct mtk_sgmii * ss,int id)153 struct phylink_pcs *mtk_sgmii_select_pcs(struct mtk_sgmii *ss, int id)
154 {
155 if (!ss->pcs[id].regmap)
156 return NULL;
157
158 return &ss->pcs[id].pcs;
159 }
160