1 /***********************license start***********************************
2 * Copyright (c) 2003-2017 Cavium Inc. ([email protected]). All rights
3 * reserved.
4 *
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are
8 * met:
9 *
10 * * Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 *
13 * * Redistributions in binary form must reproduce the above
14 * copyright notice, this list of conditions and the following
15 * disclaimer in the documentation and/or other materials provided
16 * with the distribution.
17 *
18 * * Neither the name of Cavium Inc. nor the names of
19 * its contributors may be used to endorse or promote products
20 * derived from this software without specific prior written
21 * permission.
22 *
23 * This Software, including technical data, may be subject to U.S. export
24 * control laws, including the U.S. Export Administration Act and its
25 * associated regulations, and may be subject to export or import
26 * regulations in other countries.
27 *
28 * TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
29 * AND WITH ALL FAULTS AND CAVIUM INC. MAKES NO PROMISES, REPRESENTATIONS OR
30 * WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT
31 * TO THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY
32 * REPRESENTATION OR DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT
33 * DEFECTS, AND CAVIUM SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES
34 * OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR
35 * PURPOSE, LACK OF VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT,
36 * QUIET POSSESSION OR CORRESPONDENCE TO DESCRIPTION. THE ENTIRE RISK
37 * ARISING OUT OF USE OR PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
38 ***********************license end**************************************/
39 #include <bdk.h>
40 #include <libbdk-hal/if/bdk-if.h>
41 #include <libbdk-hal/bdk-config.h>
42 #include <libbdk-hal/bdk-mdio.h>
43 #include <libbdk-hal/bdk-qlm.h>
44 #include <libbdk-hal/bdk-twsi.h>
45
46 /**
47 * Called when the PHY is connected through TWSI
48 *
49 * @param dev_node Node the ethernet device is on
50 * @param phy_addr Encoded address, see bdk-if.h for format
51 *
52 * @return Link status
53 */
__bdk_if_phy_get_twsi(bdk_node_t dev_node,int phy_addr)54 static bdk_if_link_t __bdk_if_phy_get_twsi(bdk_node_t dev_node, int phy_addr)
55 {
56 /* For TWSI:
57 Bits[31:24]: Node ID, 0xff for device node
58 Bits[23:16]: TWSI internal address width in bytes (0-2)
59 Bits[15:12]: 2=TWSI
60 Bits[11:8]: TWSI bus number
61 Bits[7:0]: TWSI address */
62 int node = (phy_addr >> 24) & 0xff;
63 int twsi_ia_width = (phy_addr >> 16) & 0xff;
64 int twsi_bus = (phy_addr >> 8) & 0xf;
65 int twsi_addr = phy_addr & 0xff;
66 if (node == 0xff)
67 node = dev_node;
68
69 bdk_if_link_t result;
70 result.u64 = 0;
71
72 /* This is from the Avago SFP 1G Module data sheet
73 Register 17 (Extended Status 1) */
74 int64_t phy_status = bdk_twsix_read_ia(node, twsi_bus, twsi_addr, 17, 2, twsi_ia_width);
75 if (phy_status != -1)
76 {
77 int speed = (phy_status >> 14)& 3;
78 int duplex = (phy_status >> 13)& 1;
79 int resolved = (phy_status >> 11)& 1;
80 int link = (phy_status >> 10)& 1;
81 if (resolved)
82 {
83 result.s.up = link;
84 result.s.full_duplex = duplex;
85 switch (speed)
86 {
87 case 0: /* 10 Mbps */
88 result.s.speed = 10;
89 break;
90 case 1: /* 100 Mbps */
91 result.s.speed = 100;
92 break;
93 case 2: /* 1 Gbps */
94 result.s.speed = 1000;
95 break;
96 case 3: /* Illegal */
97 result.u64 = 0;
98 break;
99 }
100 }
101 }
102
103 return result;
104 }
105
106 /**
107 * Read the status of a PHY
108 *
109 * @param dev_node Node the ethernet device is on
110 * @param phy_addr Encoded PHY address, see bdk-if.h for format
111 *
112 * @return Link status
113 */
__bdk_if_phy_get(bdk_node_t dev_node,int phy_addr)114 bdk_if_link_t __bdk_if_phy_get(bdk_node_t dev_node, int phy_addr)
115 {
116 int node = (phy_addr >> 24) & 0xff;
117 int mdio_bus = (phy_addr >> 8) & 0xff;
118 int mdio_addr = phy_addr & 0xff;
119 if (node == 0xff)
120 node = dev_node;
121 int phy_status;
122 bdk_if_link_t result;
123 result.u64 = 0;
124
125 /* PHY address of -1 menas there is no PHY and we should have never
126 gotten here */
127 if (phy_addr == -1)
128 return result;
129
130 /* A PHY address with the special value 0x1000 represents a PHY we can't
131 connect to through MDIO which is assumed to be at 1Gbps */
132 if (phy_addr == BDK_IF_PHY_FIXED_1GB)
133 {
134 result.s.up = 1;
135 result.s.full_duplex = 1;
136 result.s.speed = 1000;
137 return result;
138 }
139
140 /* A PHY address with the special value 0x1001 represents a PHY we can't
141 connect to through MDIO which is assumed to be at 100Mbps */
142 if (phy_addr == BDK_IF_PHY_FIXED_100MB)
143 {
144 result.s.up = 1;
145 result.s.full_duplex = 1;
146 result.s.speed = 100;
147 return result;
148 }
149
150 /* Check for a PHY connected through TWSI */
151 if ((phy_addr & BDK_IF_PHY_TYPE_MASK) == BDK_IF_PHY_TWSI)
152 return __bdk_if_phy_get_twsi(dev_node, phy_addr);
153
154 phy_status = bdk_mdio_read(node, mdio_bus, mdio_addr, BDK_MDIO_PHY_REG_ID1);
155 if ((phy_status <= 0) || (phy_status == 0xffff))
156 return result;
157
158 switch (phy_status)
159 {
160 case 0x0141: /* Marvell */
161 {
162
163 /* This code assumes we are using a Marvell Gigabit PHY. All the
164 speed information can be read from register 17 in one go. Somebody
165 using a different PHY will need to handle it above in the board
166 specific area */
167 phy_status = bdk_mdio_read(node, mdio_bus, mdio_addr, 17);
168 if (phy_status < 0)
169 return result;
170
171 /* If the resolve bit 11 isn't set, see if autoneg is turned off
172 (bit 12, reg 0). The resolve bit doesn't get set properly when
173 autoneg is off, so force it */
174 if ((phy_status & (1<<11)) == 0)
175 {
176 bdk_mdio_phy_reg_control_t control;
177 int phy_c = bdk_mdio_read(node, mdio_bus, mdio_addr, BDK_MDIO_PHY_REG_CONTROL);
178 if (phy_c < 0)
179 return result;
180 control.u16 = phy_c;
181 if (control.s.autoneg_enable == 0)
182 phy_status |= 1<<11;
183 }
184
185 /* Only return a link if the PHY has finished auto negotiation
186 and set the resolved bit (bit 11) */
187 if (phy_status & (1<<11))
188 {
189 result.s.up = 1;
190 result.s.full_duplex = ((phy_status>>13)&1);
191 switch ((phy_status>>14)&3)
192 {
193 case 0: /* 10 Mbps */
194 result.s.speed = 10;
195 break;
196 case 1: /* 100 Mbps */
197 result.s.speed = 100;
198 break;
199 case 2: /* 1 Gbps */
200 result.s.speed = 1000;
201 break;
202 case 3: /* Illegal */
203 result.u64 = 0;
204 break;
205 }
206 }
207 break;
208 }
209 case 0x0022: /* Kendin */
210 {
211 /* Register 1Fh - PHY Control */
212 /* Micrel KSZ9031RNX, EBB8104 RGMII transceiver */
213 /* Reports as "Kendin" in BDK_MDIO_PHY_REG_ID1 */
214 phy_status = bdk_mdio_read(node, mdio_bus, mdio_addr, 0x1F);
215 if (phy_status & (1 << 6)) // Speed Status - 1000Base-T
216 {
217 result.s.up = 1;
218 result.s.speed = 1000;
219 }
220 else if (phy_status & (1 << 5)) // Speed Status - 100Base-TX
221 {
222 result.s.up = 1;
223 result.s.speed = 100;
224 }
225 else if (phy_status & (1 << 4)) // Speed Status - 10Base-T
226 {
227 result.s.up = 1;
228 result.s.speed = 10;
229 }
230 if (phy_status & (1 << 3)) // Duplex Status
231 {
232 result.s.full_duplex = 1;
233 }
234 break;
235 }
236 case 0x0007: /* Vitesse */
237 {
238 /* Auxiliary Control and Status, Address 28 (0x1C) */
239 phy_status = bdk_mdio_read(node, mdio_bus, mdio_addr, 0x1c);
240 result.s.full_duplex = (phy_status>>5)&1;
241 switch ((phy_status>>3) & 3)
242 {
243 case 0:
244 result.s.speed = 10;
245 result.s.up = 1;
246 break;
247 case 1:
248 result.s.speed = 100;
249 result.s.up = 1;
250 break;
251 default:
252 result.s.speed = 1000;
253 break;
254 }
255 phy_status = bdk_mdio_read(node, mdio_bus, mdio_addr, 0x01);
256 result.s.up = (phy_status>>2)&1;
257 break;
258 }
259 default: /* Treat like Broadcom */
260 {
261 /* Below we are going to read SMI/MDIO register 0x19 which works
262 on Broadcom parts */
263 phy_status = bdk_mdio_read(node, mdio_bus, mdio_addr, 0x19);
264 if (phy_status < 0)
265 return result;
266
267 switch ((phy_status>>8) & 0x7)
268 {
269 case 0:
270 result.u64 = 0;
271 break;
272 case 1:
273 result.s.up = 1;
274 result.s.full_duplex = 0;
275 result.s.speed = 10;
276 break;
277 case 2:
278 result.s.up = 1;
279 result.s.full_duplex = 1;
280 result.s.speed = 10;
281 break;
282 case 3:
283 result.s.up = 1;
284 result.s.full_duplex = 0;
285 result.s.speed = 100;
286 break;
287 case 4:
288 result.s.up = 1;
289 result.s.full_duplex = 1;
290 result.s.speed = 100;
291 break;
292 case 5:
293 result.s.up = 1;
294 result.s.full_duplex = 1;
295 result.s.speed = 100;
296 break;
297 case 6:
298 result.s.up = 1;
299 result.s.full_duplex = 0;
300 result.s.speed = 1000;
301 break;
302 case 7:
303 result.s.up = 1;
304 result.s.full_duplex = 1;
305 result.s.speed = 1000;
306 break;
307 }
308 break;
309 }
310 }
311
312 /* If link is down, return all fields as zero. */
313 if (!result.s.up)
314 result.u64 = 0;
315
316 return result;
317 }
318
319 /**
320 * PHY XS initialization, primarily for RXAUI
321 *
322 * @param dev_node Node the ethernet device is on
323 * @param phy_addr Encoded PHY address, see bdk-if.h for format
324 *
325 * @return none
326 */
__bdk_if_phy_xs_init(bdk_node_t dev_node,int phy_addr)327 void __bdk_if_phy_xs_init(bdk_node_t dev_node, int phy_addr)
328 {
329 /* This code only supports PHYs connected through MDIO */
330 if ((phy_addr & BDK_IF_PHY_TYPE_MASK) != BDK_IF_PHY_MDIO)
331 return;
332
333 int node = (phy_addr >> 24) & 0xff;
334 int mdio_bus = (phy_addr >> 8) & 0xff;
335 int mdio_addr = phy_addr & 0xff;
336 if (node == 0xff)
337 node = dev_node;
338
339 /* Read the PMA/PMD Device Identifier (1.2, 1.3)
340 OUI is spread across both registers */
341 int dev_addr = 1;
342 int reg_addr = 2;
343 int phy_id1 = bdk_mdio_45_read(node, mdio_bus, mdio_addr, dev_addr, reg_addr);
344 if (phy_id1 == -1)
345 return;
346 reg_addr = 3;
347 int phy_id2 = bdk_mdio_45_read(node, mdio_bus, mdio_addr, dev_addr, reg_addr);
348 if (phy_id2 == -1)
349 return;
350 int model_number = (phy_id2 >> 4) & 0x3F;
351 int oui = phy_id1;
352 oui <<= 6;
353 oui |= (phy_id2 >> 10) & 0x3F;
354 switch (oui)
355 {
356 case 0x5016: /* Marvell */
357 if (model_number == 9) /* 88X3140/3120 */
358 {
359 BDK_TRACE(BGX, "N%d.MDIO%d.%d: Performing PHY reset on Marvell RXAUI PHY\n",
360 node, mdio_bus, mdio_addr);
361 dev_addr = 4;
362 reg_addr = 0;
363 /* Write bit 15, Software Reset, in PHY XS Control 1 (4.0). On CN78xx,
364 sometimes the PHY/BGX gets stuck in local fault mode, link never comes up,
365 and this appears to clear it up. Haven't seen this on CN81xx or T88,
366 but the reset seems like cheap insurance. */
367 if (bdk_mdio_45_write(node, mdio_bus, mdio_addr, dev_addr, reg_addr, (1 << 15)))
368 {
369 bdk_error("PHY XS: MDIO write to (%d.%d) failed\n", dev_addr, reg_addr);
370 return;
371 }
372
373 int reset_pending = 1;
374 while (reset_pending)
375 {
376 reset_pending = bdk_mdio_45_read(node, mdio_bus, mdio_addr, dev_addr, reg_addr);
377 reset_pending &= (1 << 15);
378 }
379
380 /* Adjust the RXAUI TX Level for Marvell PHY, per Brendan Metzner
381 write 5 to register 4.49155 */
382 reg_addr = 49155;
383 if (bdk_mdio_45_write(node, mdio_bus, mdio_addr, dev_addr, reg_addr, 5))
384 {
385 bdk_error("PHY XS: MDIO write to (%d.%d) failed\n", dev_addr, reg_addr);
386 return;
387 }
388 }
389 break;
390
391 default: /* Unknown PHY, or no PHY present */
392 break;
393 }
394 }
395
bdk_if_phy_setup(bdk_node_t dev_node)396 int bdk_if_phy_setup(bdk_node_t dev_node)
397 {
398 /* 81xx has only 2 BGX (BGX0-BGX1); BGX2 is RGMII */
399 for (int bgx = 0; bgx < 2; bgx++)
400 {
401 int port = 0;
402 int phy_addr = bdk_config_get_int(BDK_CONFIG_PHY_ADDRESS, dev_node, bgx, port);
403 if (phy_addr != -1)
404 {
405 int node = (phy_addr >> 24) & 0xff;
406 int mdio_bus = (phy_addr >> 8) & 0xff;
407 int mdio_addr = phy_addr & 0xff;
408 if (node == 0xff)
409 node = bdk_numa_local();
410 if ((phy_addr & BDK_IF_PHY_TYPE_MASK) == BDK_IF_PHY_MDIO)
411 {
412 int qlm = bdk_qlm_get_qlm_num(node, BDK_IF_BGX, bgx, port);
413 if (qlm == -1)
414 continue;
415
416 BDK_TRACE(PHY, "N%d.BGX%d.%d: Configuring ...\n", node, bgx, port);
417
418 /* Check PHY id */
419 int phy_status_1 = bdk_mdio_read(node, mdio_bus, phy_addr, BDK_MDIO_PHY_REG_ID1);
420 int phy_status_2 = bdk_mdio_read(node, mdio_bus, phy_addr, BDK_MDIO_PHY_REG_ID2);
421
422 /* Vitesse */
423 if (phy_status_1 == 0x0007)
424 {
425 if (phy_status_2 == 0x0670)
426 {
427 bdk_if_phy_vsc8514_setup(node, qlm, mdio_bus, mdio_addr);
428 }
429 else
430 {
431 bdk_if_phy_vetesse_setup(node, qlm, mdio_bus, mdio_addr);
432 }
433 }
434
435 /* Marvell */
436 else if (phy_status_1 == 0x0141)
437 bdk_if_phy_marvell_setup(node, qlm, mdio_bus, mdio_addr);
438 else
439 BDK_TRACE(PHY, "N%d.BGX%d.%d: Unknown PHY %x\n", node, bgx, port, phy_status_1);
440 }
441 }
442 }
443 return 0;
444 }
445
446