net/ngbe: fix external PHY power down
[dpdk.git] / drivers / net / ngbe / base / ngbe_phy_mvl.c
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
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(&reg, &reg22);
24
25         ngbe_read_phy_reg_mdi(hw, reg22.addr, reg22.device_type, phy_data);
26
27         return 0;
28 }
29
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(&reg, &reg22);
45
46         ngbe_write_phy_reg_mdi(hw, reg22.addr, reg22.device_type, phy_data);
47
48         return 0;
49 }
50
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
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         hw->phy.set_phy_power(hw, false);
125
126         return ret_val;
127 }
128
129 s32 ngbe_setup_phy_link_mvl(struct ngbe_hw *hw, u32 speed,
130                                 bool autoneg_wait_to_complete)
131 {
132         u16 value_r4 = 0;
133         u16 value_r9 = 0;
134         u16 value;
135
136         UNREFERENCED_PARAMETER(autoneg_wait_to_complete);
137
138         if (hw->led_conf == 0xFFFF) {
139                 /* LED control */
140                 ngbe_write_phy_reg_mdi(hw, MVL_PAGE_SEL, 0, 3);
141                 ngbe_read_phy_reg_mdi(hw, MVL_LEDFCR, 0, &value);
142                 value &= ~(MVL_LEDFCR_CTL0 | MVL_LEDFCR_CTL1);
143                 value |= MVL_LEDFCR_CTL0_CONF | MVL_LEDFCR_CTL1_CONF;
144                 ngbe_write_phy_reg_mdi(hw, MVL_LEDFCR, 0, value);
145                 ngbe_read_phy_reg_mdi(hw, MVL_LEDPCR, 0, &value);
146                 value &= ~(MVL_LEDPCR_CTL0 | MVL_LEDPCR_CTL1);
147                 value |= MVL_LEDPCR_CTL0_CONF | MVL_LEDPCR_CTL1_CONF;
148                 ngbe_write_phy_reg_mdi(hw, MVL_LEDPCR, 0, value);
149         }
150
151         hw->phy.autoneg_advertised = 0;
152
153         if (hw->phy.type == ngbe_phy_mvl) {
154                 if (!hw->mac.autoneg) {
155                         switch (speed) {
156                         case NGBE_LINK_SPEED_1GB_FULL:
157                                 value = MVL_CTRL_SPEED_SELECT1;
158                                 break;
159                         case NGBE_LINK_SPEED_100M_FULL:
160                                 value = MVL_CTRL_SPEED_SELECT0;
161                                 break;
162                         case NGBE_LINK_SPEED_10M_FULL:
163                                 value = 0;
164                                 break;
165                         default:
166                                 value = MVL_CTRL_SPEED_SELECT0 |
167                                         MVL_CTRL_SPEED_SELECT1;
168                                 DEBUGOUT("unknown speed = 0x%x.", speed);
169                                 break;
170                         }
171                         /* duplex full */
172                         value |= MVL_CTRL_DUPLEX | MVL_CTRL_RESET;
173                         ngbe_write_phy_reg_mdi(hw, MVL_CTRL, 0, value);
174
175                         goto skip_an;
176                 }
177                 if (speed & NGBE_LINK_SPEED_1GB_FULL) {
178                         value_r9 |= MVL_PHY_1000BASET_FULL;
179                         hw->phy.autoneg_advertised |= NGBE_LINK_SPEED_1GB_FULL;
180                 }
181
182                 if (speed & NGBE_LINK_SPEED_100M_FULL) {
183                         value_r4 |= MVL_PHY_100BASET_FULL;
184                         hw->phy.autoneg_advertised |= NGBE_LINK_SPEED_100M_FULL;
185                 }
186
187                 if (speed & NGBE_LINK_SPEED_10M_FULL) {
188                         value_r4 |= MVL_PHY_10BASET_FULL;
189                         hw->phy.autoneg_advertised |= NGBE_LINK_SPEED_10M_FULL;
190                 }
191
192                 hw->phy.read_reg(hw, MVL_ANA, 0, &value);
193                 value &= ~(MVL_PHY_100BASET_FULL |
194                            MVL_PHY_100BASET_HALF |
195                            MVL_PHY_10BASET_FULL |
196                            MVL_PHY_10BASET_HALF);
197                 value_r4 |= value;
198                 hw->phy.write_reg(hw, MVL_ANA, 0, value_r4);
199
200                 hw->phy.read_reg(hw, MVL_PHY_1000BASET, 0, &value);
201                 value &= ~(MVL_PHY_1000BASET_FULL |
202                            MVL_PHY_1000BASET_HALF);
203                 value_r9 |= value;
204                 hw->phy.write_reg(hw, MVL_PHY_1000BASET, 0, value_r9);
205         } else {
206                 hw->phy.autoneg_advertised |= NGBE_LINK_SPEED_1GB_FULL;
207
208                 hw->phy.read_reg(hw, MVL_ANA, 0, &value);
209                 value &= ~(MVL_PHY_1000BASEX_HALF | MVL_PHY_1000BASEX_FULL);
210                 value |= MVL_PHY_1000BASEX_FULL;
211                 hw->phy.write_reg(hw, MVL_ANA, 0, value);
212         }
213
214         value = MVL_CTRL_RESTART_AN | MVL_CTRL_ANE | MVL_CTRL_RESET;
215         ngbe_write_phy_reg_mdi(hw, MVL_CTRL, 0, value);
216
217 skip_an:
218         hw->phy.set_phy_power(hw, true);
219
220         hw->phy.read_reg(hw, MVL_INTR, 0, &value);
221
222         return 0;
223 }
224
225 s32 ngbe_reset_phy_mvl(struct ngbe_hw *hw)
226 {
227         u32 i;
228         u16 ctrl = 0;
229         s32 status = 0;
230
231         if (hw->phy.type != ngbe_phy_mvl && hw->phy.type != ngbe_phy_mvl_sfi)
232                 return NGBE_ERR_PHY_TYPE;
233
234         /* select page 18 reg 20 */
235         status = ngbe_write_phy_reg_mdi(hw, MVL_PAGE_SEL, 0, 18);
236
237         /* mode select to RGMII-to-copper or RGMII-to-sfi*/
238         if (hw->phy.type == ngbe_phy_mvl)
239                 ctrl = MVL_GEN_CTL_MODE_COPPER;
240         else
241                 ctrl = MVL_GEN_CTL_MODE_FIBER;
242         status = ngbe_write_phy_reg_mdi(hw, MVL_GEN_CTL, 0, ctrl);
243         /* mode reset */
244         ctrl |= MVL_GEN_CTL_RESET;
245         status = ngbe_write_phy_reg_mdi(hw, MVL_GEN_CTL, 0, ctrl);
246
247         for (i = 0; i < MVL_PHY_RST_WAIT_PERIOD; i++) {
248                 status = ngbe_read_phy_reg_mdi(hw, MVL_GEN_CTL, 0, &ctrl);
249                 if (!(ctrl & MVL_GEN_CTL_RESET))
250                         break;
251                 msleep(1);
252         }
253
254         if (i == MVL_PHY_RST_WAIT_PERIOD) {
255                 DEBUGOUT("PHY reset polling failed to complete.");
256                 return NGBE_ERR_RESET_FAILED;
257         }
258
259         return status;
260 }
261
262 s32 ngbe_get_phy_advertised_pause_mvl(struct ngbe_hw *hw, u8 *pause_bit)
263 {
264         u16 value;
265         s32 status = 0;
266
267         if (hw->phy.type == ngbe_phy_mvl) {
268                 status = hw->phy.read_reg(hw, MVL_ANA, 0, &value);
269                 value &= MVL_CANA_ASM_PAUSE | MVL_CANA_PAUSE;
270                 *pause_bit = (u8)(value >> 10);
271         } else {
272                 status = hw->phy.read_reg(hw, MVL_ANA, 0, &value);
273                 value &= MVL_FANA_PAUSE_MASK;
274                 *pause_bit = (u8)(value >> 7);
275         }
276
277         return status;
278 }
279
280 s32 ngbe_get_phy_lp_advertised_pause_mvl(struct ngbe_hw *hw, u8 *pause_bit)
281 {
282         u16 value;
283         s32 status = 0;
284
285         if (hw->phy.type == ngbe_phy_mvl) {
286                 status = hw->phy.read_reg(hw, MVL_LPAR, 0, &value);
287                 value &= MVL_CLPAR_ASM_PAUSE | MVL_CLPAR_PAUSE;
288                 *pause_bit = (u8)(value >> 10);
289         } else {
290                 status = hw->phy.read_reg(hw, MVL_LPAR, 0, &value);
291                 value &= MVL_FLPAR_PAUSE_MASK;
292                 *pause_bit = (u8)(value >> 7);
293         }
294
295         return status;
296 }
297
298 s32 ngbe_set_phy_pause_adv_mvl(struct ngbe_hw *hw, u16 pause_bit)
299 {
300         u16 value;
301         s32 status = 0;
302
303         if (hw->phy.type == ngbe_phy_mvl) {
304                 status = hw->phy.read_reg(hw, MVL_ANA, 0, &value);
305                 value &= ~(MVL_CANA_ASM_PAUSE | MVL_CANA_PAUSE);
306         } else {
307                 status = hw->phy.read_reg(hw, MVL_ANA, 0, &value);
308                 value &= ~MVL_FANA_PAUSE_MASK;
309         }
310
311         value |= pause_bit;
312         status = hw->phy.write_reg(hw, MVL_ANA, 0, value);
313
314         return status;
315 }
316
317 s32 ngbe_check_phy_link_mvl(struct ngbe_hw *hw,
318                 u32 *speed, bool *link_up)
319 {
320         s32 status = 0;
321         u16 phy_link = 0;
322         u16 phy_speed = 0;
323         u16 phy_data = 0;
324         u16 insr = 0;
325
326         /* Initialize speed and link to default case */
327         *link_up = false;
328         *speed = NGBE_LINK_SPEED_UNKNOWN;
329
330         hw->phy.read_reg(hw, MVL_INTR, 0, &insr);
331
332         /*
333          * Check current speed and link status of the PHY register.
334          * This is a vendor specific register and may have to
335          * be changed for other copper PHYs.
336          */
337         status = hw->phy.read_reg(hw, MVL_PHYSR, 0, &phy_data);
338         phy_link = phy_data & MVL_PHYSR_LINK;
339         phy_speed = phy_data & MVL_PHYSR_SPEED_MASK;
340
341         if (phy_link == MVL_PHYSR_LINK) {
342                 *link_up = true;
343
344                 if (phy_speed == MVL_PHYSR_SPEED_1000M)
345                         *speed = NGBE_LINK_SPEED_1GB_FULL;
346                 else if (phy_speed == MVL_PHYSR_SPEED_100M)
347                         *speed = NGBE_LINK_SPEED_100M_FULL;
348                 else if (phy_speed == MVL_PHYSR_SPEED_10M)
349                         *speed = NGBE_LINK_SPEED_10M_FULL;
350         }
351
352         return status;
353 }
354
355 s32 ngbe_set_phy_power_mvl(struct ngbe_hw *hw, bool on)
356 {
357         u16 value = 0;
358
359         hw->phy.read_reg(hw, MVL_CTRL, 0, &value);
360         if (on)
361                 value &= ~MVL_CTRL_PWDN;
362         else
363                 value |= MVL_CTRL_PWDN;
364         hw->phy.write_reg(hw, MVL_CTRL, 0, value);
365
366         return 0;
367 }