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 | |
15 | struct mtk_eth_muxc { |
16 | const char *name; |
17 | int cap_bit; |
18 | int (*set_path)(struct mtk_eth *eth, u64 path); |
19 | }; |
20 | |
21 | static 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 | |
43 | static 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 | |
76 | static 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 | |
99 | static 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 | |
130 | static 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 | |
168 | static 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 | |
199 | static 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 | |
223 | static 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 | |
248 | out: |
249 | return err; |
250 | } |
251 | |
252 | int 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 | |
263 | int 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 | |
277 | int 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 | |