diff options
Diffstat (limited to 'arch/mips/cavium-octeon/executive/cvmx-helper-sgmii.c')
-rw-r--r-- | arch/mips/cavium-octeon/executive/cvmx-helper-sgmii.c | 554 |
1 files changed, 554 insertions, 0 deletions
diff --git a/arch/mips/cavium-octeon/executive/cvmx-helper-sgmii.c b/arch/mips/cavium-octeon/executive/cvmx-helper-sgmii.c new file mode 100644 index 000000000000..0c0bf5d30e70 --- /dev/null +++ b/arch/mips/cavium-octeon/executive/cvmx-helper-sgmii.c | |||
@@ -0,0 +1,554 @@ | |||
1 | /***********************license start*************** | ||
2 | * Author: Cavium Networks | ||
3 | * | ||
4 | * Contact: support@caviumnetworks.com | ||
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> | ||
39 | #include <asm/octeon/cvmx-helper-board.h> | ||
40 | |||
41 | #include <asm/octeon/cvmx-gmxx-defs.h> | ||
42 | #include <asm/octeon/cvmx-pcsx-defs.h> | ||
43 | |||
44 | void __cvmx_interrupt_gmxx_enable(int interface); | ||
45 | void __cvmx_interrupt_pcsx_intx_en_reg_enable(int index, int block); | ||
46 | void __cvmx_interrupt_pcsxx_int_en_reg_enable(int index); | ||
47 | |||
48 | /** | ||
49 | * Perform initialization required only once for an SGMII port. | ||
50 | * | ||
51 | * @interface: Interface to init | ||
52 | * @index: Index of prot on the interface | ||
53 | * | ||
54 | * Returns Zero on success, negative on failure | ||
55 | */ | ||
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 | |||
132 | /** | ||
133 | * Initialize the SERTES link for the first time or after a loss | ||
134 | * of link. | ||
135 | * | ||
136 | * @interface: Interface to init | ||
137 | * @index: Index of prot on the interface | ||
138 | * | ||
139 | * Returns Zero on success, negative on failure | ||
140 | */ | ||
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)); | ||
155 | if (cvmx_sysinfo_get()->board_type != CVMX_BOARD_TYPE_SIM) { | ||
156 | control_reg.s.reset = 1; | ||
157 | cvmx_write_csr(CVMX_PCSX_MRX_CONTROL_REG(index, interface), | ||
158 | control_reg.u64); | ||
159 | if (CVMX_WAIT_FOR_FIELD64 | ||
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 | */ | ||
185 | if ((cvmx_sysinfo_get()->board_type != CVMX_BOARD_TYPE_SIM) && | ||
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 | |||
195 | /** | ||
196 | * Configure an SGMII link to the specified speed after the SERTES | ||
197 | * link is up. | ||
198 | * | ||
199 | * @interface: Interface to init | ||
200 | * @index: Index of prot on the interface | ||
201 | * @link_info: Link state to configure | ||
202 | * | ||
203 | * Returns Zero on success, negative on failure | ||
204 | */ | ||
205 | static int __cvmx_helper_sgmii_hardware_init_link_speed(int interface, | ||
206 | int index, | ||
207 | cvmx_helper_link_info_t | ||
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 */ | ||
221 | if (CVMX_WAIT_FOR_FIELD64 | ||
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)) { | ||
227 | cvmx_dprintf | ||
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 | |||
301 | /** | ||
302 | * Bring up the SGMII interface to be ready for packet I/O but | ||
303 | * leave I/O disabled using the GMX override. This function | ||
304 | * follows the bringup documented in 10.6.3 of the manual. | ||
305 | * | ||
306 | * @interface: Interface to bringup | ||
307 | * @num_ports: Number of ports on the interface | ||
308 | * | ||
309 | * Returns Zero on success, negative on failure | ||
310 | */ | ||
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); | ||
320 | __cvmx_helper_sgmii_link_set(ipd_port, | ||
321 | __cvmx_helper_sgmii_link_get | ||
322 | (ipd_port)); | ||
323 | |||
324 | } | ||
325 | |||
326 | return 0; | ||
327 | } | ||
328 | |||
329 | int __cvmx_helper_sgmii_enumerate(int interface) | ||
330 | { | ||
331 | return 4; | ||
332 | } | ||
333 | /** | ||
334 | * Probe a SGMII interface and determine the number of ports | ||
335 | * connected to it. The SGMII interface should still be down after | ||
336 | * this call. | ||
337 | * | ||
338 | * @interface: Interface to probe | ||
339 | * | ||
340 | * Returns Number of ports on the interface. Zero to disable. | ||
341 | */ | ||
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 | |||
357 | /** | ||
358 | * Bringup and enable a SGMII interface. After this call packet | ||
359 | * I/O should be fully functional. This is called with IPD | ||
360 | * enabled but PKO disabled. | ||
361 | * | ||
362 | * @interface: Interface to bring up | ||
363 | * | ||
364 | * Returns Zero on success, negative on failure | ||
365 | */ | ||
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); | ||
380 | __cvmx_interrupt_pcsx_intx_en_reg_enable(index, interface); | ||
381 | } | ||
382 | __cvmx_interrupt_pcsxx_int_en_reg_enable(interface); | ||
383 | __cvmx_interrupt_gmxx_enable(interface); | ||
384 | return 0; | ||
385 | } | ||
386 | |||
387 | /** | ||
388 | * Return the link state of an IPD/PKO port as returned by | ||
389 | * auto negotiation. The result of this function may not match | ||
390 | * Octeon's link config if auto negotiation has changed since | ||
391 | * the last call to cvmx_helper_link_set(). | ||
392 | * | ||
393 | * @ipd_port: IPD/PKO port to query | ||
394 | * | ||
395 | * Returns Link state | ||
396 | */ | ||
397 | cvmx_helper_link_info_t __cvmx_helper_sgmii_link_get(int ipd_port) | ||
398 | { | ||
399 | cvmx_helper_link_info_t result; | ||
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 | |||
407 | if (cvmx_sysinfo_get()->board_type == CVMX_BOARD_TYPE_SIM) { | ||
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 | |||
496 | /** | ||
497 | * Configure an IPD/PKO port for the specified link state. This | ||
498 | * function does not influence auto negotiation at the PHY level. | ||
499 | * The passed link state must always match the link state returned | ||
500 | * by cvmx_helper_link_get(). It is normally best to use | ||
501 | * cvmx_helper_link_autoconf() instead. | ||
502 | * | ||
503 | * @ipd_port: IPD/PKO port to configure | ||
504 | * @link_info: The new link state | ||
505 | * | ||
506 | * Returns Zero on success, negative on failure | ||
507 | */ | ||
508 | int __cvmx_helper_sgmii_link_set(int ipd_port, | ||
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 | |||
518 | /** | ||
519 | * Configure a port for internal and/or external loopback. Internal | ||
520 | * loopback causes packets sent by the port to be received by | ||
521 | * Octeon. External loopback causes packets received from the wire to | ||
522 | * sent out again. | ||
523 | * | ||
524 | * @ipd_port: IPD/PKO port to loopback. | ||
525 | * @enable_internal: | ||
526 | * Non zero if you want internal loopback | ||
527 | * @enable_external: | ||
528 | * Non zero if you want external loopback | ||
529 | * | ||
530 | * Returns Zero on success, negative on failure. | ||
531 | */ | ||
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 | } | ||