Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
cvmx-helper-sgmii.c
Go to the documentation of this file.
1 /***********************license start***************
2  * Author: Cavium Networks
3  *
4  * Contact: [email protected]
5  * This file is part of the OCTEON SDK
6  *
7  * Copyright (c) 2003-2008 Cavium Networks
8  *
9  * This file is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License, Version 2, as
11  * published by the Free Software Foundation.
12  *
13  * This file is distributed in the hope that it will be useful, but
14  * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
15  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
16  * NONINFRINGEMENT. See the GNU General Public License for more
17  * details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this file; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
22  * or visit http://www.gnu.org/licenses/.
23  *
24  * This file may also be available under a different license from Cavium.
25  * Contact Cavium Networks for more information
26  ***********************license end**************************************/
27 
28 /*
29  * Functions for SGMII initialization, configuration,
30  * and monitoring.
31  */
32 
33 #include <asm/octeon/octeon.h>
34 
35 #include <asm/octeon/cvmx-config.h>
36 
37 #include <asm/octeon/cvmx-mdio.h>
38 #include <asm/octeon/cvmx-helper.h>
40 
43 
47 
56 static int __cvmx_helper_sgmii_hardware_init_one_time(int interface, int index)
57 {
58  const uint64_t clock_mhz = cvmx_sysinfo_get()->cpu_clock_hz / 1000000;
59  union cvmx_pcsx_miscx_ctl_reg pcs_misc_ctl_reg;
60  union cvmx_pcsx_linkx_timer_count_reg pcsx_linkx_timer_count_reg;
61  union cvmx_gmxx_prtx_cfg gmxx_prtx_cfg;
62 
63  /* Disable GMX */
64  gmxx_prtx_cfg.u64 = cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface));
65  gmxx_prtx_cfg.s.en = 0;
66  cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), gmxx_prtx_cfg.u64);
67 
68  /*
69  * Write PCS*_LINK*_TIMER_COUNT_REG[COUNT] with the
70  * appropriate value. 1000BASE-X specifies a 10ms
71  * interval. SGMII specifies a 1.6ms interval.
72  */
73  pcs_misc_ctl_reg.u64 =
74  cvmx_read_csr(CVMX_PCSX_MISCX_CTL_REG(index, interface));
75  pcsx_linkx_timer_count_reg.u64 =
76  cvmx_read_csr(CVMX_PCSX_LINKX_TIMER_COUNT_REG(index, interface));
77  if (pcs_misc_ctl_reg.s.mode) {
78  /* 1000BASE-X */
79  pcsx_linkx_timer_count_reg.s.count =
80  (10000ull * clock_mhz) >> 10;
81  } else {
82  /* SGMII */
83  pcsx_linkx_timer_count_reg.s.count =
84  (1600ull * clock_mhz) >> 10;
85  }
86  cvmx_write_csr(CVMX_PCSX_LINKX_TIMER_COUNT_REG(index, interface),
87  pcsx_linkx_timer_count_reg.u64);
88 
89  /*
90  * Write the advertisement register to be used as the
91  * tx_Config_Reg<D15:D0> of the autonegotiation. In
92  * 1000BASE-X mode, tx_Config_Reg<D15:D0> is PCS*_AN*_ADV_REG.
93  * In SGMII PHY mode, tx_Config_Reg<D15:D0> is
94  * PCS*_SGM*_AN_ADV_REG. In SGMII MAC mode,
95  * tx_Config_Reg<D15:D0> is the fixed value 0x4001, so this
96  * step can be skipped.
97  */
98  if (pcs_misc_ctl_reg.s.mode) {
99  /* 1000BASE-X */
100  union cvmx_pcsx_anx_adv_reg pcsx_anx_adv_reg;
101  pcsx_anx_adv_reg.u64 =
102  cvmx_read_csr(CVMX_PCSX_ANX_ADV_REG(index, interface));
103  pcsx_anx_adv_reg.s.rem_flt = 0;
104  pcsx_anx_adv_reg.s.pause = 3;
105  pcsx_anx_adv_reg.s.hfd = 1;
106  pcsx_anx_adv_reg.s.fd = 1;
107  cvmx_write_csr(CVMX_PCSX_ANX_ADV_REG(index, interface),
108  pcsx_anx_adv_reg.u64);
109  } else {
110  union cvmx_pcsx_miscx_ctl_reg pcsx_miscx_ctl_reg;
111  pcsx_miscx_ctl_reg.u64 =
112  cvmx_read_csr(CVMX_PCSX_MISCX_CTL_REG(index, interface));
113  if (pcsx_miscx_ctl_reg.s.mac_phy) {
114  /* PHY Mode */
115  union cvmx_pcsx_sgmx_an_adv_reg pcsx_sgmx_an_adv_reg;
116  pcsx_sgmx_an_adv_reg.u64 =
117  cvmx_read_csr(CVMX_PCSX_SGMX_AN_ADV_REG
118  (index, interface));
119  pcsx_sgmx_an_adv_reg.s.link = 1;
120  pcsx_sgmx_an_adv_reg.s.dup = 1;
121  pcsx_sgmx_an_adv_reg.s.speed = 2;
122  cvmx_write_csr(CVMX_PCSX_SGMX_AN_ADV_REG
123  (index, interface),
124  pcsx_sgmx_an_adv_reg.u64);
125  } else {
126  /* MAC Mode - Nothing to do */
127  }
128  }
129  return 0;
130 }
131 
141 static int __cvmx_helper_sgmii_hardware_init_link(int interface, int index)
142 {
143  union cvmx_pcsx_mrx_control_reg control_reg;
144 
145  /*
146  * Take PCS through a reset sequence.
147  * PCS*_MR*_CONTROL_REG[PWR_DN] should be cleared to zero.
148  * Write PCS*_MR*_CONTROL_REG[RESET]=1 (while not changing the
149  * value of the other PCS*_MR*_CONTROL_REG bits). Read
150  * PCS*_MR*_CONTROL_REG[RESET] until it changes value to
151  * zero.
152  */
153  control_reg.u64 =
154  cvmx_read_csr(CVMX_PCSX_MRX_CONTROL_REG(index, interface));
156  control_reg.s.reset = 1;
157  cvmx_write_csr(CVMX_PCSX_MRX_CONTROL_REG(index, interface),
158  control_reg.u64);
160  (CVMX_PCSX_MRX_CONTROL_REG(index, interface),
161  union cvmx_pcsx_mrx_control_reg, reset, ==, 0, 10000)) {
162  cvmx_dprintf("SGMII%d: Timeout waiting for port %d "
163  "to finish reset\n",
164  interface, index);
165  return -1;
166  }
167  }
168 
169  /*
170  * Write PCS*_MR*_CONTROL_REG[RST_AN]=1 to ensure a fresh
171  * sgmii negotiation starts.
172  */
173  control_reg.s.rst_an = 1;
174  control_reg.s.an_en = 1;
175  control_reg.s.pwr_dn = 0;
176  cvmx_write_csr(CVMX_PCSX_MRX_CONTROL_REG(index, interface),
177  control_reg.u64);
178 
179  /*
180  * Wait for PCS*_MR*_STATUS_REG[AN_CPT] to be set, indicating
181  * that sgmii autonegotiation is complete. In MAC mode this
182  * isn't an ethernet link, but a link between Octeon and the
183  * PHY.
184  */
186  CVMX_WAIT_FOR_FIELD64(CVMX_PCSX_MRX_STATUS_REG(index, interface),
187  union cvmx_pcsx_mrx_status_reg, an_cpt, ==, 1,
188  10000)) {
189  /* cvmx_dprintf("SGMII%d: Port %d link timeout\n", interface, index); */
190  return -1;
191  }
192  return 0;
193 }
194 
205 static int __cvmx_helper_sgmii_hardware_init_link_speed(int interface,
206  int index,
208  link_info)
209 {
210  int is_enabled;
211  union cvmx_gmxx_prtx_cfg gmxx_prtx_cfg;
212  union cvmx_pcsx_miscx_ctl_reg pcsx_miscx_ctl_reg;
213 
214  /* Disable GMX before we make any changes. Remember the enable state */
215  gmxx_prtx_cfg.u64 = cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface));
216  is_enabled = gmxx_prtx_cfg.s.en;
217  gmxx_prtx_cfg.s.en = 0;
218  cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), gmxx_prtx_cfg.u64);
219 
220  /* Wait for GMX to be idle */
222  (CVMX_GMXX_PRTX_CFG(index, interface), union cvmx_gmxx_prtx_cfg,
223  rx_idle, ==, 1, 10000)
224  || CVMX_WAIT_FOR_FIELD64(CVMX_GMXX_PRTX_CFG(index, interface),
225  union cvmx_gmxx_prtx_cfg, tx_idle, ==, 1,
226  10000)) {
228  ("SGMII%d: Timeout waiting for port %d to be idle\n",
229  interface, index);
230  return -1;
231  }
232 
233  /* Read GMX CFG again to make sure the disable completed */
234  gmxx_prtx_cfg.u64 = cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface));
235 
236  /*
237  * Get the misc control for PCS. We will need to set the
238  * duplication amount.
239  */
240  pcsx_miscx_ctl_reg.u64 =
241  cvmx_read_csr(CVMX_PCSX_MISCX_CTL_REG(index, interface));
242 
243  /*
244  * Use GMXENO to force the link down if the status we get says
245  * it should be down.
246  */
247  pcsx_miscx_ctl_reg.s.gmxeno = !link_info.s.link_up;
248 
249  /* Only change the duplex setting if the link is up */
250  if (link_info.s.link_up)
251  gmxx_prtx_cfg.s.duplex = link_info.s.full_duplex;
252 
253  /* Do speed based setting for GMX */
254  switch (link_info.s.speed) {
255  case 10:
256  gmxx_prtx_cfg.s.speed = 0;
257  gmxx_prtx_cfg.s.speed_msb = 1;
258  gmxx_prtx_cfg.s.slottime = 0;
259  /* Setting from GMX-603 */
260  pcsx_miscx_ctl_reg.s.samp_pt = 25;
261  cvmx_write_csr(CVMX_GMXX_TXX_SLOT(index, interface), 64);
262  cvmx_write_csr(CVMX_GMXX_TXX_BURST(index, interface), 0);
263  break;
264  case 100:
265  gmxx_prtx_cfg.s.speed = 0;
266  gmxx_prtx_cfg.s.speed_msb = 0;
267  gmxx_prtx_cfg.s.slottime = 0;
268  pcsx_miscx_ctl_reg.s.samp_pt = 0x5;
269  cvmx_write_csr(CVMX_GMXX_TXX_SLOT(index, interface), 64);
270  cvmx_write_csr(CVMX_GMXX_TXX_BURST(index, interface), 0);
271  break;
272  case 1000:
273  gmxx_prtx_cfg.s.speed = 1;
274  gmxx_prtx_cfg.s.speed_msb = 0;
275  gmxx_prtx_cfg.s.slottime = 1;
276  pcsx_miscx_ctl_reg.s.samp_pt = 1;
277  cvmx_write_csr(CVMX_GMXX_TXX_SLOT(index, interface), 512);
278  cvmx_write_csr(CVMX_GMXX_TXX_BURST(index, interface), 8192);
279  break;
280  default:
281  break;
282  }
283 
284  /* Write the new misc control for PCS */
285  cvmx_write_csr(CVMX_PCSX_MISCX_CTL_REG(index, interface),
286  pcsx_miscx_ctl_reg.u64);
287 
288  /* Write the new GMX settings with the port still disabled */
289  cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), gmxx_prtx_cfg.u64);
290 
291  /* Read GMX CFG again to make sure the config completed */
292  gmxx_prtx_cfg.u64 = cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface));
293 
294  /* Restore the enabled / disabled state */
295  gmxx_prtx_cfg.s.en = is_enabled;
296  cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), gmxx_prtx_cfg.u64);
297 
298  return 0;
299 }
300 
311 static int __cvmx_helper_sgmii_hardware_init(int interface, int num_ports)
312 {
313  int index;
314 
315  __cvmx_helper_setup_gmx(interface, num_ports);
316 
317  for (index = 0; index < num_ports; index++) {
318  int ipd_port = cvmx_helper_get_ipd_port(interface, index);
319  __cvmx_helper_sgmii_hardware_init_one_time(interface, index);
322  (ipd_port));
323 
324  }
325 
326  return 0;
327 }
328 
330 {
331  return 4;
332 }
342 int __cvmx_helper_sgmii_probe(int interface)
343 {
344  union cvmx_gmxx_inf_mode mode;
345 
346  /*
347  * Due to errata GMX-700 on CN56XXp1.x and CN52XXp1.x, the
348  * interface needs to be enabled before IPD otherwise per port
349  * backpressure may not work properly
350  */
351  mode.u64 = cvmx_read_csr(CVMX_GMXX_INF_MODE(interface));
352  mode.s.en = 1;
353  cvmx_write_csr(CVMX_GMXX_INF_MODE(interface), mode.u64);
354  return __cvmx_helper_sgmii_enumerate(interface);
355 }
356 
366 int __cvmx_helper_sgmii_enable(int interface)
367 {
368  int num_ports = cvmx_helper_ports_on_interface(interface);
369  int index;
370 
371  __cvmx_helper_sgmii_hardware_init(interface, num_ports);
372 
373  for (index = 0; index < num_ports; index++) {
374  union cvmx_gmxx_prtx_cfg gmxx_prtx_cfg;
375  gmxx_prtx_cfg.u64 =
376  cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface));
377  gmxx_prtx_cfg.s.en = 1;
378  cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface),
379  gmxx_prtx_cfg.u64);
381  }
383  __cvmx_interrupt_gmxx_enable(interface);
384  return 0;
385 }
386 
398 {
400  union cvmx_pcsx_miscx_ctl_reg pcs_misc_ctl_reg;
401  int interface = cvmx_helper_get_interface_num(ipd_port);
402  int index = cvmx_helper_get_interface_index_num(ipd_port);
403  union cvmx_pcsx_mrx_control_reg pcsx_mrx_control_reg;
404 
405  result.u64 = 0;
406 
408  /* The simulator gives you a simulated 1Gbps full duplex link */
409  result.s.link_up = 1;
410  result.s.full_duplex = 1;
411  result.s.speed = 1000;
412  return result;
413  }
414 
415  pcsx_mrx_control_reg.u64 =
416  cvmx_read_csr(CVMX_PCSX_MRX_CONTROL_REG(index, interface));
417  if (pcsx_mrx_control_reg.s.loopbck1) {
418  /* Force 1Gbps full duplex link for internal loopback */
419  result.s.link_up = 1;
420  result.s.full_duplex = 1;
421  result.s.speed = 1000;
422  return result;
423  }
424 
425  pcs_misc_ctl_reg.u64 =
426  cvmx_read_csr(CVMX_PCSX_MISCX_CTL_REG(index, interface));
427  if (pcs_misc_ctl_reg.s.mode) {
428  /* 1000BASE-X */
429  /* FIXME */
430  } else {
431  union cvmx_pcsx_miscx_ctl_reg pcsx_miscx_ctl_reg;
432  pcsx_miscx_ctl_reg.u64 =
433  cvmx_read_csr(CVMX_PCSX_MISCX_CTL_REG(index, interface));
434  if (pcsx_miscx_ctl_reg.s.mac_phy) {
435  /* PHY Mode */
436  union cvmx_pcsx_mrx_status_reg pcsx_mrx_status_reg;
437  union cvmx_pcsx_anx_results_reg pcsx_anx_results_reg;
438 
439  /*
440  * Don't bother continuing if the SERTES low
441  * level link is down
442  */
443  pcsx_mrx_status_reg.u64 =
444  cvmx_read_csr(CVMX_PCSX_MRX_STATUS_REG
445  (index, interface));
446  if (pcsx_mrx_status_reg.s.lnk_st == 0) {
447  if (__cvmx_helper_sgmii_hardware_init_link
448  (interface, index) != 0)
449  return result;
450  }
451 
452  /* Read the autoneg results */
453  pcsx_anx_results_reg.u64 =
454  cvmx_read_csr(CVMX_PCSX_ANX_RESULTS_REG
455  (index, interface));
456  if (pcsx_anx_results_reg.s.an_cpt) {
457  /*
458  * Auto negotiation is complete. Set
459  * status accordingly.
460  */
461  result.s.full_duplex =
462  pcsx_anx_results_reg.s.dup;
463  result.s.link_up =
464  pcsx_anx_results_reg.s.link_ok;
465  switch (pcsx_anx_results_reg.s.spd) {
466  case 0:
467  result.s.speed = 10;
468  break;
469  case 1:
470  result.s.speed = 100;
471  break;
472  case 2:
473  result.s.speed = 1000;
474  break;
475  default:
476  result.s.speed = 0;
477  result.s.link_up = 0;
478  break;
479  }
480  } else {
481  /*
482  * Auto negotiation isn't
483  * complete. Return link down.
484  */
485  result.s.speed = 0;
486  result.s.link_up = 0;
487  }
488  } else { /* MAC Mode */
489 
490  result = __cvmx_helper_board_link_get(ipd_port);
491  }
492  }
493  return result;
494 }
495 
509  cvmx_helper_link_info_t link_info)
510 {
511  int interface = cvmx_helper_get_interface_num(ipd_port);
512  int index = cvmx_helper_get_interface_index_num(ipd_port);
513  __cvmx_helper_sgmii_hardware_init_link(interface, index);
514  return __cvmx_helper_sgmii_hardware_init_link_speed(interface, index,
515  link_info);
516 }
517 
532 int __cvmx_helper_sgmii_configure_loopback(int ipd_port, int enable_internal,
533  int enable_external)
534 {
535  int interface = cvmx_helper_get_interface_num(ipd_port);
536  int index = cvmx_helper_get_interface_index_num(ipd_port);
537  union cvmx_pcsx_mrx_control_reg pcsx_mrx_control_reg;
538  union cvmx_pcsx_miscx_ctl_reg pcsx_miscx_ctl_reg;
539 
540  pcsx_mrx_control_reg.u64 =
541  cvmx_read_csr(CVMX_PCSX_MRX_CONTROL_REG(index, interface));
542  pcsx_mrx_control_reg.s.loopbck1 = enable_internal;
543  cvmx_write_csr(CVMX_PCSX_MRX_CONTROL_REG(index, interface),
544  pcsx_mrx_control_reg.u64);
545 
546  pcsx_miscx_ctl_reg.u64 =
547  cvmx_read_csr(CVMX_PCSX_MISCX_CTL_REG(index, interface));
548  pcsx_miscx_ctl_reg.s.loopbck2 = enable_external;
549  cvmx_write_csr(CVMX_PCSX_MISCX_CTL_REG(index, interface),
550  pcsx_miscx_ctl_reg.u64);
551 
552  __cvmx_helper_sgmii_hardware_init_link(interface, index);
553  return 0;
554 }