1 /* SPDX-License-Identifier: BSD-3-Clause
2 * Copyright(c) 2018-2021 Beijing WangXun Technology Co., Ltd.
3 */
4
5 #include "ngbe_phy_mvl.h"
6
7 #define MVL_PHY_RST_WAIT_PERIOD 5
8
ngbe_read_phy_reg_mvl(struct ngbe_hw * hw,u32 reg_addr,u32 device_type,u16 * phy_data)9 s32 ngbe_read_phy_reg_mvl(struct ngbe_hw *hw,
10 u32 reg_addr, u32 device_type, u16 *phy_data)
11 {
12 mdi_reg_t reg;
13 mdi_reg_22_t reg22;
14
15 reg.device_type = device_type;
16 reg.addr = reg_addr;
17
18 if (hw->phy.media_type == ngbe_media_type_fiber)
19 ngbe_write_phy_reg_mdi(hw, MVL_PAGE_SEL, 0, 1);
20 else
21 ngbe_write_phy_reg_mdi(hw, MVL_PAGE_SEL, 0, 0);
22
23 ngbe_mdi_map_register(®, ®22);
24
25 ngbe_read_phy_reg_mdi(hw, reg22.addr, reg22.device_type, phy_data);
26
27 return 0;
28 }
29
ngbe_write_phy_reg_mvl(struct ngbe_hw * hw,u32 reg_addr,u32 device_type,u16 phy_data)30 s32 ngbe_write_phy_reg_mvl(struct ngbe_hw *hw,
31 u32 reg_addr, u32 device_type, u16 phy_data)
32 {
33 mdi_reg_t reg;
34 mdi_reg_22_t reg22;
35
36 reg.device_type = device_type;
37 reg.addr = reg_addr;
38
39 if (hw->phy.media_type == ngbe_media_type_fiber)
40 ngbe_write_phy_reg_mdi(hw, MVL_PAGE_SEL, 0, 1);
41 else
42 ngbe_write_phy_reg_mdi(hw, MVL_PAGE_SEL, 0, 0);
43
44 ngbe_mdi_map_register(®, ®22);
45
46 ngbe_write_phy_reg_mdi(hw, reg22.addr, reg22.device_type, phy_data);
47
48 return 0;
49 }
50
ngbe_check_phy_mode_mvl(struct ngbe_hw * hw)51 s32 ngbe_check_phy_mode_mvl(struct ngbe_hw *hw)
52 {
53 u16 value = 0;
54
55 /* select page 18 reg 20 */
56 ngbe_write_phy_reg_mdi(hw, MVL_PAGE_SEL, 0, 18);
57 ngbe_read_phy_reg_mdi(hw, MVL_GEN_CTL, 0, &value);
58 if (MVL_GEN_CTL_MODE(value) == MVL_GEN_CTL_MODE_COPPER) {
59 /* mode select to RGMII-to-copper */
60 hw->phy.type = ngbe_phy_mvl;
61 hw->phy.media_type = ngbe_media_type_copper;
62 hw->mac.link_type = ngbe_link_copper;
63 } else if (MVL_GEN_CTL_MODE(value) == MVL_GEN_CTL_MODE_FIBER) {
64 /* mode select to RGMII-to-sfi */
65 hw->phy.type = ngbe_phy_mvl_sfi;
66 hw->phy.media_type = ngbe_media_type_fiber;
67 hw->mac.link_type = ngbe_link_fiber;
68 } else {
69 DEBUGOUT("marvell 88E1512 mode %x is not supported.", value);
70 return NGBE_ERR_DEVICE_NOT_SUPPORTED;
71 }
72
73 return 0;
74 }
75
ngbe_init_phy_mvl(struct ngbe_hw * hw)76 s32 ngbe_init_phy_mvl(struct ngbe_hw *hw)
77 {
78 s32 ret_val = 0;
79 u16 value = 0;
80 int i;
81
82 /* enable interrupts, only link status change and an done is allowed */
83 ngbe_write_phy_reg_mdi(hw, MVL_PAGE_SEL, 0, 2);
84 ngbe_read_phy_reg_mdi(hw, MVL_RGM_CTL2, 0, &value);
85 value &= ~MVL_RGM_CTL2_TTC;
86 value |= MVL_RGM_CTL2_RTC;
87 ngbe_write_phy_reg_mdi(hw, MVL_RGM_CTL2, 0, value);
88
89 hw->phy.write_reg(hw, MVL_CTRL, 0, MVL_CTRL_RESET);
90 for (i = 0; i < 15; i++) {
91 ngbe_read_phy_reg_mdi(hw, MVL_CTRL, 0, &value);
92 if (value & MVL_CTRL_RESET)
93 msleep(1);
94 else
95 break;
96 }
97
98 if (i == 15) {
99 DEBUGOUT("phy reset exceeds maximum waiting period.");
100 return NGBE_ERR_TIMEOUT;
101 }
102
103 ret_val = hw->phy.reset_hw(hw);
104 if (ret_val)
105 return ret_val;
106
107 /* set LED2 to interrupt output and INTn active low */
108 ngbe_write_phy_reg_mdi(hw, MVL_PAGE_SEL, 0, 3);
109 ngbe_read_phy_reg_mdi(hw, MVL_LEDTCR, 0, &value);
110 value |= MVL_LEDTCR_INTR_EN;
111 value &= ~(MVL_LEDTCR_INTR_POL);
112 ngbe_write_phy_reg_mdi(hw, MVL_LEDTCR, 0, value);
113
114 if (hw->phy.type == ngbe_phy_mvl_sfi) {
115 hw->phy.read_reg(hw, MVL_CTRL1, 0, &value);
116 value &= ~MVL_CTRL1_INTR_POL;
117 ngbe_write_phy_reg_mdi(hw, MVL_CTRL1, 0, value);
118 }
119
120 /* enable link status change and AN complete interrupts */
121 value = MVL_INTR_EN_ANC | MVL_INTR_EN_LSC;
122 hw->phy.write_reg(hw, MVL_INTR_EN, 0, value);
123
124 ngbe_read_phy_reg_mdi(hw, MVL_CTRL, 0, &value);
125 value |= MVL_CTRL_PWDN;
126 ngbe_write_phy_reg_mdi(hw, MVL_CTRL, 0, value);
127
128 return ret_val;
129 }
130
ngbe_setup_phy_link_mvl(struct ngbe_hw * hw,u32 speed,bool autoneg_wait_to_complete)131 s32 ngbe_setup_phy_link_mvl(struct ngbe_hw *hw, u32 speed,
132 bool autoneg_wait_to_complete)
133 {
134 u16 value_r4 = 0;
135 u16 value_r9 = 0;
136 u16 value;
137
138 UNREFERENCED_PARAMETER(autoneg_wait_to_complete);
139
140 if (hw->led_conf == 0xFFFF) {
141 /* LED control */
142 ngbe_write_phy_reg_mdi(hw, MVL_PAGE_SEL, 0, 3);
143 ngbe_read_phy_reg_mdi(hw, MVL_LEDFCR, 0, &value);
144 value &= ~(MVL_LEDFCR_CTL0 | MVL_LEDFCR_CTL1);
145 value |= MVL_LEDFCR_CTL0_CONF | MVL_LEDFCR_CTL1_CONF;
146 ngbe_write_phy_reg_mdi(hw, MVL_LEDFCR, 0, value);
147 ngbe_read_phy_reg_mdi(hw, MVL_LEDPCR, 0, &value);
148 value &= ~(MVL_LEDPCR_CTL0 | MVL_LEDPCR_CTL1);
149 value |= MVL_LEDPCR_CTL0_CONF | MVL_LEDPCR_CTL1_CONF;
150 ngbe_write_phy_reg_mdi(hw, MVL_LEDPCR, 0, value);
151 }
152
153 hw->phy.autoneg_advertised = 0;
154
155 if (hw->phy.type == ngbe_phy_mvl) {
156 if (!hw->mac.autoneg) {
157 switch (speed) {
158 case NGBE_LINK_SPEED_1GB_FULL:
159 value = MVL_CTRL_SPEED_SELECT1;
160 break;
161 case NGBE_LINK_SPEED_100M_FULL:
162 value = MVL_CTRL_SPEED_SELECT0;
163 break;
164 case NGBE_LINK_SPEED_10M_FULL:
165 value = 0;
166 break;
167 default:
168 value = MVL_CTRL_SPEED_SELECT0 |
169 MVL_CTRL_SPEED_SELECT1;
170 DEBUGOUT("unknown speed = 0x%x.", speed);
171 break;
172 }
173 /* duplex full */
174 value |= MVL_CTRL_DUPLEX | MVL_CTRL_RESET;
175 ngbe_write_phy_reg_mdi(hw, MVL_CTRL, 0, value);
176
177 goto skip_an;
178 }
179 if (speed & NGBE_LINK_SPEED_1GB_FULL) {
180 value_r9 |= MVL_PHY_1000BASET_FULL;
181 hw->phy.autoneg_advertised |= NGBE_LINK_SPEED_1GB_FULL;
182 }
183
184 if (speed & NGBE_LINK_SPEED_100M_FULL) {
185 value_r4 |= MVL_PHY_100BASET_FULL;
186 hw->phy.autoneg_advertised |= NGBE_LINK_SPEED_100M_FULL;
187 }
188
189 if (speed & NGBE_LINK_SPEED_10M_FULL) {
190 value_r4 |= MVL_PHY_10BASET_FULL;
191 hw->phy.autoneg_advertised |= NGBE_LINK_SPEED_10M_FULL;
192 }
193
194 hw->phy.read_reg(hw, MVL_ANA, 0, &value);
195 value &= ~(MVL_PHY_100BASET_FULL |
196 MVL_PHY_100BASET_HALF |
197 MVL_PHY_10BASET_FULL |
198 MVL_PHY_10BASET_HALF);
199 value_r4 |= value;
200 hw->phy.write_reg(hw, MVL_ANA, 0, value_r4);
201
202 hw->phy.read_reg(hw, MVL_PHY_1000BASET, 0, &value);
203 value &= ~(MVL_PHY_1000BASET_FULL |
204 MVL_PHY_1000BASET_HALF);
205 value_r9 |= value;
206 hw->phy.write_reg(hw, MVL_PHY_1000BASET, 0, value_r9);
207 } else {
208 hw->phy.autoneg_advertised = 1;
209
210 hw->phy.read_reg(hw, MVL_ANA, 0, &value);
211 value &= ~(MVL_PHY_1000BASEX_HALF | MVL_PHY_1000BASEX_FULL);
212 value |= MVL_PHY_1000BASEX_FULL;
213 hw->phy.write_reg(hw, MVL_ANA, 0, value);
214 }
215
216 value = MVL_CTRL_RESTART_AN | MVL_CTRL_ANE | MVL_CTRL_RESET;
217 ngbe_write_phy_reg_mdi(hw, MVL_CTRL, 0, value);
218
219 skip_an:
220 ngbe_read_phy_reg_mdi(hw, MVL_CTRL, 0, &value);
221 value |= MVL_CTRL_PWDN;
222 ngbe_write_phy_reg_mdi(hw, MVL_CTRL, 0, value);
223
224 hw->phy.read_reg(hw, MVL_INTR, 0, &value);
225
226 return 0;
227 }
228
ngbe_reset_phy_mvl(struct ngbe_hw * hw)229 s32 ngbe_reset_phy_mvl(struct ngbe_hw *hw)
230 {
231 u32 i;
232 u16 ctrl = 0;
233 s32 status = 0;
234
235 if (hw->phy.type != ngbe_phy_mvl && hw->phy.type != ngbe_phy_mvl_sfi)
236 return NGBE_ERR_PHY_TYPE;
237
238 /* select page 18 reg 20 */
239 status = ngbe_write_phy_reg_mdi(hw, MVL_PAGE_SEL, 0, 18);
240
241 /* mode select to RGMII-to-copper or RGMII-to-sfi*/
242 if (hw->phy.type == ngbe_phy_mvl)
243 ctrl = MVL_GEN_CTL_MODE_COPPER;
244 else
245 ctrl = MVL_GEN_CTL_MODE_FIBER;
246 status = ngbe_write_phy_reg_mdi(hw, MVL_GEN_CTL, 0, ctrl);
247 /* mode reset */
248 ctrl |= MVL_GEN_CTL_RESET;
249 status = ngbe_write_phy_reg_mdi(hw, MVL_GEN_CTL, 0, ctrl);
250
251 for (i = 0; i < MVL_PHY_RST_WAIT_PERIOD; i++) {
252 status = ngbe_read_phy_reg_mdi(hw, MVL_GEN_CTL, 0, &ctrl);
253 if (!(ctrl & MVL_GEN_CTL_RESET))
254 break;
255 msleep(1);
256 }
257
258 if (i == MVL_PHY_RST_WAIT_PERIOD) {
259 DEBUGOUT("PHY reset polling failed to complete.");
260 return NGBE_ERR_RESET_FAILED;
261 }
262
263 return status;
264 }
265
ngbe_get_phy_advertised_pause_mvl(struct ngbe_hw * hw,u8 * pause_bit)266 s32 ngbe_get_phy_advertised_pause_mvl(struct ngbe_hw *hw, u8 *pause_bit)
267 {
268 u16 value;
269 s32 status = 0;
270
271 if (hw->phy.type == ngbe_phy_mvl) {
272 status = hw->phy.read_reg(hw, MVL_ANA, 0, &value);
273 value &= MVL_CANA_ASM_PAUSE | MVL_CANA_PAUSE;
274 *pause_bit = (u8)(value >> 10);
275 } else {
276 status = hw->phy.read_reg(hw, MVL_ANA, 0, &value);
277 value &= MVL_FANA_PAUSE_MASK;
278 *pause_bit = (u8)(value >> 7);
279 }
280
281 return status;
282 }
283
ngbe_get_phy_lp_advertised_pause_mvl(struct ngbe_hw * hw,u8 * pause_bit)284 s32 ngbe_get_phy_lp_advertised_pause_mvl(struct ngbe_hw *hw, u8 *pause_bit)
285 {
286 u16 value;
287 s32 status = 0;
288
289 if (hw->phy.type == ngbe_phy_mvl) {
290 status = hw->phy.read_reg(hw, MVL_LPAR, 0, &value);
291 value &= MVL_CLPAR_ASM_PAUSE | MVL_CLPAR_PAUSE;
292 *pause_bit = (u8)(value >> 10);
293 } else {
294 status = hw->phy.read_reg(hw, MVL_LPAR, 0, &value);
295 value &= MVL_FLPAR_PAUSE_MASK;
296 *pause_bit = (u8)(value >> 7);
297 }
298
299 return status;
300 }
301
ngbe_set_phy_pause_adv_mvl(struct ngbe_hw * hw,u16 pause_bit)302 s32 ngbe_set_phy_pause_adv_mvl(struct ngbe_hw *hw, u16 pause_bit)
303 {
304 u16 value;
305 s32 status = 0;
306
307 if (hw->phy.type == ngbe_phy_mvl) {
308 status = hw->phy.read_reg(hw, MVL_ANA, 0, &value);
309 value &= ~(MVL_CANA_ASM_PAUSE | MVL_CANA_PAUSE);
310 } else {
311 status = hw->phy.read_reg(hw, MVL_ANA, 0, &value);
312 value &= ~MVL_FANA_PAUSE_MASK;
313 }
314
315 value |= pause_bit;
316 status = hw->phy.write_reg(hw, MVL_ANA, 0, value);
317
318 return status;
319 }
320
ngbe_check_phy_link_mvl(struct ngbe_hw * hw,u32 * speed,bool * link_up)321 s32 ngbe_check_phy_link_mvl(struct ngbe_hw *hw,
322 u32 *speed, bool *link_up)
323 {
324 s32 status = 0;
325 u16 phy_link = 0;
326 u16 phy_speed = 0;
327 u16 phy_data = 0;
328 u16 insr = 0;
329
330 /* Initialize speed and link to default case */
331 *link_up = false;
332 *speed = NGBE_LINK_SPEED_UNKNOWN;
333
334 hw->phy.read_reg(hw, MVL_INTR, 0, &insr);
335
336 /*
337 * Check current speed and link status of the PHY register.
338 * This is a vendor specific register and may have to
339 * be changed for other copper PHYs.
340 */
341 status = hw->phy.read_reg(hw, MVL_PHYSR, 0, &phy_data);
342 phy_link = phy_data & MVL_PHYSR_LINK;
343 phy_speed = phy_data & MVL_PHYSR_SPEED_MASK;
344
345 if (phy_link == MVL_PHYSR_LINK) {
346 *link_up = true;
347
348 if (phy_speed == MVL_PHYSR_SPEED_1000M)
349 *speed = NGBE_LINK_SPEED_1GB_FULL;
350 else if (phy_speed == MVL_PHYSR_SPEED_100M)
351 *speed = NGBE_LINK_SPEED_100M_FULL;
352 else if (phy_speed == MVL_PHYSR_SPEED_10M)
353 *speed = NGBE_LINK_SPEED_10M_FULL;
354 }
355
356 return status;
357 }
358
359