1// SPDX-License-Identifier: GPL-2.0
2// Copyright (c) 2018-2019 MediaTek Inc.
3
4/* A library for configuring path from GMAC/GDM to target PHY
5 *
6 * Author: Sean Wang <sean.wang@mediatek.com>
7 *
8 */
9
10#include <linux/phy.h>
11#include <linux/regmap.h>
12
13#include "mtk_eth_soc.h"
14
15struct mtk_eth_muxc {
16 const char *name;
17 int cap_bit;
18 int (*set_path)(struct mtk_eth *eth, u64 path);
19};
20
21static const char *mtk_eth_path_name(u64 path)
22{
23 switch (path) {
24 case MTK_ETH_PATH_GMAC1_RGMII:
25 return "gmac1_rgmii";
26 case MTK_ETH_PATH_GMAC1_TRGMII:
27 return "gmac1_trgmii";
28 case MTK_ETH_PATH_GMAC1_SGMII:
29 return "gmac1_sgmii";
30 case MTK_ETH_PATH_GMAC2_RGMII:
31 return "gmac2_rgmii";
32 case MTK_ETH_PATH_GMAC2_SGMII:
33 return "gmac2_sgmii";
34 case MTK_ETH_PATH_GMAC2_GEPHY:
35 return "gmac2_gephy";
36 case MTK_ETH_PATH_GDM1_ESW:
37 return "gdm1_esw";
38 default:
39 return "unknown path";
40 }
41}
42
43static int set_mux_gdm1_to_gmac1_esw(struct mtk_eth *eth, u64 path)
44{
45 bool updated = true;
46 u32 mask, set, reg;
47
48 switch (path) {
49 case MTK_ETH_PATH_GMAC1_SGMII:
50 mask = ~(u32)MTK_MUX_TO_ESW;
51 set = 0;
52 break;
53 case MTK_ETH_PATH_GDM1_ESW:
54 mask = ~(u32)MTK_MUX_TO_ESW;
55 set = MTK_MUX_TO_ESW;
56 break;
57 default:
58 updated = false;
59 break;
60 }
61
62 if (mtk_is_netsys_v3_or_greater(eth))
63 reg = MTK_MAC_MISC_V3;
64 else
65 reg = MTK_MAC_MISC;
66
67 if (updated)
68 mtk_m32(eth, mask, set, reg);
69
70 dev_dbg(eth->dev, "path %s in %s updated = %d\n",
71 mtk_eth_path_name(path), __func__, updated);
72
73 return 0;
74}
75
76static int set_mux_gmac2_gmac0_to_gephy(struct mtk_eth *eth, u64 path)
77{
78 unsigned int val = 0;
79 bool updated = true;
80
81 switch (path) {
82 case MTK_ETH_PATH_GMAC2_GEPHY:
83 val = ~(u32)GEPHY_MAC_SEL;
84 break;
85 default:
86 updated = false;
87 break;
88 }
89
90 if (updated)
91 regmap_update_bits(map: eth->infra, INFRA_MISC2, GEPHY_MAC_SEL, val);
92
93 dev_dbg(eth->dev, "path %s in %s updated = %d\n",
94 mtk_eth_path_name(path), __func__, updated);
95
96 return 0;
97}
98
99static int set_mux_u3_gmac2_to_qphy(struct mtk_eth *eth, u64 path)
100{
101 unsigned int val = 0, mask = 0, reg = 0;
102 bool updated = true;
103
104 switch (path) {
105 case MTK_ETH_PATH_GMAC2_SGMII:
106 if (MTK_HAS_CAPS(eth->soc->caps, MTK_U3_COPHY_V2)) {
107 reg = USB_PHY_SWITCH_REG;
108 val = SGMII_QPHY_SEL;
109 mask = QPHY_SEL_MASK;
110 } else {
111 reg = INFRA_MISC2;
112 val = CO_QPHY_SEL;
113 mask = val;
114 }
115 break;
116 default:
117 updated = false;
118 break;
119 }
120
121 if (updated)
122 regmap_update_bits(map: eth->infra, reg, mask, val);
123
124 dev_dbg(eth->dev, "path %s in %s updated = %d\n",
125 mtk_eth_path_name(path), __func__, updated);
126
127 return 0;
128}
129
130static int set_mux_gmac1_gmac2_to_sgmii_rgmii(struct mtk_eth *eth, u64 path)
131{
132 unsigned int val = 0;
133 bool updated = true;
134
135 switch (path) {
136 case MTK_ETH_PATH_GMAC1_SGMII:
137 val = SYSCFG0_SGMII_GMAC1;
138 break;
139 case MTK_ETH_PATH_GMAC2_SGMII:
140 val = SYSCFG0_SGMII_GMAC2;
141 break;
142 case MTK_ETH_PATH_GMAC1_RGMII:
143 case MTK_ETH_PATH_GMAC2_RGMII:
144 regmap_read(map: eth->ethsys, ETHSYS_SYSCFG0, val: &val);
145 val &= SYSCFG0_SGMII_MASK;
146
147 if ((path == MTK_GMAC1_RGMII && val == SYSCFG0_SGMII_GMAC1) ||
148 (path == MTK_GMAC2_RGMII && val == SYSCFG0_SGMII_GMAC2))
149 val = 0;
150 else
151 updated = false;
152 break;
153 default:
154 updated = false;
155 break;
156 }
157
158 if (updated)
159 regmap_update_bits(map: eth->ethsys, ETHSYS_SYSCFG0,
160 SYSCFG0_SGMII_MASK, val);
161
162 dev_dbg(eth->dev, "path %s in %s updated = %d\n",
163 mtk_eth_path_name(path), __func__, updated);
164
165 return 0;
166}
167
168static int set_mux_gmac12_to_gephy_sgmii(struct mtk_eth *eth, u64 path)
169{
170 unsigned int val = 0;
171 bool updated = true;
172
173 regmap_read(map: eth->ethsys, ETHSYS_SYSCFG0, val: &val);
174
175 switch (path) {
176 case MTK_ETH_PATH_GMAC1_SGMII:
177 val |= SYSCFG0_SGMII_GMAC1_V2;
178 break;
179 case MTK_ETH_PATH_GMAC2_GEPHY:
180 val &= ~(u32)SYSCFG0_SGMII_GMAC2_V2;
181 break;
182 case MTK_ETH_PATH_GMAC2_SGMII:
183 val |= SYSCFG0_SGMII_GMAC2_V2;
184 break;
185 default:
186 updated = false;
187 }
188
189 if (updated)
190 regmap_update_bits(map: eth->ethsys, ETHSYS_SYSCFG0,
191 SYSCFG0_SGMII_MASK, val);
192
193 dev_dbg(eth->dev, "path %s in %s updated = %d\n",
194 mtk_eth_path_name(path), __func__, updated);
195
196 return 0;
197}
198
199static const struct mtk_eth_muxc mtk_eth_muxc[] = {
200 {
201 .name = "mux_gdm1_to_gmac1_esw",
202 .cap_bit = MTK_ETH_MUX_GDM1_TO_GMAC1_ESW,
203 .set_path = set_mux_gdm1_to_gmac1_esw,
204 }, {
205 .name = "mux_gmac2_gmac0_to_gephy",
206 .cap_bit = MTK_ETH_MUX_GMAC2_GMAC0_TO_GEPHY,
207 .set_path = set_mux_gmac2_gmac0_to_gephy,
208 }, {
209 .name = "mux_u3_gmac2_to_qphy",
210 .cap_bit = MTK_ETH_MUX_U3_GMAC2_TO_QPHY,
211 .set_path = set_mux_u3_gmac2_to_qphy,
212 }, {
213 .name = "mux_gmac1_gmac2_to_sgmii_rgmii",
214 .cap_bit = MTK_ETH_MUX_GMAC1_GMAC2_TO_SGMII_RGMII,
215 .set_path = set_mux_gmac1_gmac2_to_sgmii_rgmii,
216 }, {
217 .name = "mux_gmac12_to_gephy_sgmii",
218 .cap_bit = MTK_ETH_MUX_GMAC12_TO_GEPHY_SGMII,
219 .set_path = set_mux_gmac12_to_gephy_sgmii,
220 },
221};
222
223static int mtk_eth_mux_setup(struct mtk_eth *eth, u64 path)
224{
225 int i, err = 0;
226
227 if (!MTK_HAS_CAPS(eth->soc->caps, path)) {
228 dev_err(eth->dev, "path %s isn't support on the SoC\n",
229 mtk_eth_path_name(path));
230 return -EINVAL;
231 }
232
233 if (!MTK_HAS_CAPS(eth->soc->caps, MTK_MUX))
234 return 0;
235
236 /* Setup MUX in path fabric */
237 for (i = 0; i < ARRAY_SIZE(mtk_eth_muxc); i++) {
238 if (MTK_HAS_CAPS(eth->soc->caps, mtk_eth_muxc[i].cap_bit)) {
239 err = mtk_eth_muxc[i].set_path(eth, path);
240 if (err)
241 goto out;
242 } else {
243 dev_dbg(eth->dev, "mux %s isn't present on the SoC\n",
244 mtk_eth_muxc[i].name);
245 }
246 }
247
248out:
249 return err;
250}
251
252int mtk_gmac_sgmii_path_setup(struct mtk_eth *eth, int mac_id)
253{
254 u64 path;
255
256 path = (mac_id == 0) ? MTK_ETH_PATH_GMAC1_SGMII :
257 MTK_ETH_PATH_GMAC2_SGMII;
258
259 /* Setup proper MUXes along the path */
260 return mtk_eth_mux_setup(eth, path);
261}
262
263int mtk_gmac_gephy_path_setup(struct mtk_eth *eth, int mac_id)
264{
265 u64 path = 0;
266
267 if (mac_id == 1)
268 path = MTK_ETH_PATH_GMAC2_GEPHY;
269
270 if (!path)
271 return -EINVAL;
272
273 /* Setup proper MUXes along the path */
274 return mtk_eth_mux_setup(eth, path);
275}
276
277int mtk_gmac_rgmii_path_setup(struct mtk_eth *eth, int mac_id)
278{
279 u64 path;
280
281 path = (mac_id == 0) ? MTK_ETH_PATH_GMAC1_RGMII :
282 MTK_ETH_PATH_GMAC2_RGMII;
283
284 /* Setup proper MUXes along the path */
285 return mtk_eth_mux_setup(eth, path);
286}
287
288

source code of linux/drivers/net/ethernet/mediatek/mtk_eth_path.c