Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/arch/mips/cavium-octeon/executive/cvmx-helper-sgmii.c
26481 views
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-2018 Cavium, Inc.
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-helper.h>
38
#include <asm/octeon/cvmx-helper-board.h>
39
40
#include <asm/octeon/cvmx-gmxx-defs.h>
41
#include <asm/octeon/cvmx-pcsx-defs.h>
42
#include <asm/octeon/cvmx-pcsxx-defs.h>
43
44
/**
45
* Perform initialization required only once for an SGMII port.
46
*
47
* @interface: Interface to init
48
* @index: Index of prot on the interface
49
*
50
* Returns Zero on success, negative on failure
51
*/
52
static int __cvmx_helper_sgmii_hardware_init_one_time(int interface, int index)
53
{
54
const uint64_t clock_mhz = cvmx_sysinfo_get()->cpu_clock_hz / 1000000;
55
union cvmx_pcsx_miscx_ctl_reg pcs_misc_ctl_reg;
56
union cvmx_pcsx_linkx_timer_count_reg pcsx_linkx_timer_count_reg;
57
union cvmx_gmxx_prtx_cfg gmxx_prtx_cfg;
58
59
/* Disable GMX */
60
gmxx_prtx_cfg.u64 = cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface));
61
gmxx_prtx_cfg.s.en = 0;
62
cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), gmxx_prtx_cfg.u64);
63
64
/*
65
* Write PCS*_LINK*_TIMER_COUNT_REG[COUNT] with the
66
* appropriate value. 1000BASE-X specifies a 10ms
67
* interval. SGMII specifies a 1.6ms interval.
68
*/
69
pcs_misc_ctl_reg.u64 =
70
cvmx_read_csr(CVMX_PCSX_MISCX_CTL_REG(index, interface));
71
pcsx_linkx_timer_count_reg.u64 =
72
cvmx_read_csr(CVMX_PCSX_LINKX_TIMER_COUNT_REG(index, interface));
73
if (pcs_misc_ctl_reg.s.mode) {
74
/* 1000BASE-X */
75
pcsx_linkx_timer_count_reg.s.count =
76
(10000ull * clock_mhz) >> 10;
77
} else {
78
/* SGMII */
79
pcsx_linkx_timer_count_reg.s.count =
80
(1600ull * clock_mhz) >> 10;
81
}
82
cvmx_write_csr(CVMX_PCSX_LINKX_TIMER_COUNT_REG(index, interface),
83
pcsx_linkx_timer_count_reg.u64);
84
85
/*
86
* Write the advertisement register to be used as the
87
* tx_Config_Reg<D15:D0> of the autonegotiation. In
88
* 1000BASE-X mode, tx_Config_Reg<D15:D0> is PCS*_AN*_ADV_REG.
89
* In SGMII PHY mode, tx_Config_Reg<D15:D0> is
90
* PCS*_SGM*_AN_ADV_REG. In SGMII MAC mode,
91
* tx_Config_Reg<D15:D0> is the fixed value 0x4001, so this
92
* step can be skipped.
93
*/
94
if (pcs_misc_ctl_reg.s.mode) {
95
/* 1000BASE-X */
96
union cvmx_pcsx_anx_adv_reg pcsx_anx_adv_reg;
97
pcsx_anx_adv_reg.u64 =
98
cvmx_read_csr(CVMX_PCSX_ANX_ADV_REG(index, interface));
99
pcsx_anx_adv_reg.s.rem_flt = 0;
100
pcsx_anx_adv_reg.s.pause = 3;
101
pcsx_anx_adv_reg.s.hfd = 1;
102
pcsx_anx_adv_reg.s.fd = 1;
103
cvmx_write_csr(CVMX_PCSX_ANX_ADV_REG(index, interface),
104
pcsx_anx_adv_reg.u64);
105
} else {
106
union cvmx_pcsx_miscx_ctl_reg pcsx_miscx_ctl_reg;
107
pcsx_miscx_ctl_reg.u64 =
108
cvmx_read_csr(CVMX_PCSX_MISCX_CTL_REG(index, interface));
109
if (pcsx_miscx_ctl_reg.s.mac_phy) {
110
/* PHY Mode */
111
union cvmx_pcsx_sgmx_an_adv_reg pcsx_sgmx_an_adv_reg;
112
pcsx_sgmx_an_adv_reg.u64 =
113
cvmx_read_csr(CVMX_PCSX_SGMX_AN_ADV_REG
114
(index, interface));
115
pcsx_sgmx_an_adv_reg.s.link = 1;
116
pcsx_sgmx_an_adv_reg.s.dup = 1;
117
pcsx_sgmx_an_adv_reg.s.speed = 2;
118
cvmx_write_csr(CVMX_PCSX_SGMX_AN_ADV_REG
119
(index, interface),
120
pcsx_sgmx_an_adv_reg.u64);
121
} else {
122
/* MAC Mode - Nothing to do */
123
}
124
}
125
return 0;
126
}
127
128
/**
129
* Initialize the SERTES link for the first time or after a loss
130
* of link.
131
*
132
* @interface: Interface to init
133
* @index: Index of prot on the interface
134
*
135
* Returns Zero on success, negative on failure
136
*/
137
static int __cvmx_helper_sgmii_hardware_init_link(int interface, int index)
138
{
139
union cvmx_pcsx_mrx_control_reg control_reg;
140
141
/*
142
* Take PCS through a reset sequence.
143
* PCS*_MR*_CONTROL_REG[PWR_DN] should be cleared to zero.
144
* Write PCS*_MR*_CONTROL_REG[RESET]=1 (while not changing the
145
* value of the other PCS*_MR*_CONTROL_REG bits). Read
146
* PCS*_MR*_CONTROL_REG[RESET] until it changes value to
147
* zero.
148
*/
149
control_reg.u64 =
150
cvmx_read_csr(CVMX_PCSX_MRX_CONTROL_REG(index, interface));
151
if (cvmx_sysinfo_get()->board_type != CVMX_BOARD_TYPE_SIM) {
152
control_reg.s.reset = 1;
153
cvmx_write_csr(CVMX_PCSX_MRX_CONTROL_REG(index, interface),
154
control_reg.u64);
155
if (CVMX_WAIT_FOR_FIELD64
156
(CVMX_PCSX_MRX_CONTROL_REG(index, interface),
157
union cvmx_pcsx_mrx_control_reg, reset, ==, 0, 10000)) {
158
cvmx_dprintf("SGMII%d: Timeout waiting for port %d "
159
"to finish reset\n",
160
interface, index);
161
return -1;
162
}
163
}
164
165
/*
166
* Write PCS*_MR*_CONTROL_REG[RST_AN]=1 to ensure a fresh
167
* sgmii negotiation starts.
168
*/
169
control_reg.s.rst_an = 1;
170
control_reg.s.an_en = 1;
171
control_reg.s.pwr_dn = 0;
172
cvmx_write_csr(CVMX_PCSX_MRX_CONTROL_REG(index, interface),
173
control_reg.u64);
174
175
/*
176
* Wait for PCS*_MR*_STATUS_REG[AN_CPT] to be set, indicating
177
* that sgmii autonegotiation is complete. In MAC mode this
178
* isn't an ethernet link, but a link between Octeon and the
179
* PHY.
180
*/
181
if ((cvmx_sysinfo_get()->board_type != CVMX_BOARD_TYPE_SIM) &&
182
CVMX_WAIT_FOR_FIELD64(CVMX_PCSX_MRX_STATUS_REG(index, interface),
183
union cvmx_pcsx_mrx_status_reg, an_cpt, ==, 1,
184
10000)) {
185
/* cvmx_dprintf("SGMII%d: Port %d link timeout\n", interface, index); */
186
return -1;
187
}
188
return 0;
189
}
190
191
/**
192
* Configure an SGMII link to the specified speed after the SERTES
193
* link is up.
194
*
195
* @interface: Interface to init
196
* @index: Index of prot on the interface
197
* @link_info: Link state to configure
198
*
199
* Returns Zero on success, negative on failure
200
*/
201
static int __cvmx_helper_sgmii_hardware_init_link_speed(int interface,
202
int index,
203
union cvmx_helper_link_info
204
link_info)
205
{
206
int is_enabled;
207
union cvmx_gmxx_prtx_cfg gmxx_prtx_cfg;
208
union cvmx_pcsx_miscx_ctl_reg pcsx_miscx_ctl_reg;
209
210
/* Disable GMX before we make any changes. Remember the enable state */
211
gmxx_prtx_cfg.u64 = cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface));
212
is_enabled = gmxx_prtx_cfg.s.en;
213
gmxx_prtx_cfg.s.en = 0;
214
cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), gmxx_prtx_cfg.u64);
215
216
/* Wait for GMX to be idle */
217
if (CVMX_WAIT_FOR_FIELD64
218
(CVMX_GMXX_PRTX_CFG(index, interface), union cvmx_gmxx_prtx_cfg,
219
rx_idle, ==, 1, 10000)
220
|| CVMX_WAIT_FOR_FIELD64(CVMX_GMXX_PRTX_CFG(index, interface),
221
union cvmx_gmxx_prtx_cfg, tx_idle, ==, 1,
222
10000)) {
223
cvmx_dprintf
224
("SGMII%d: Timeout waiting for port %d to be idle\n",
225
interface, index);
226
return -1;
227
}
228
229
/* Read GMX CFG again to make sure the disable completed */
230
gmxx_prtx_cfg.u64 = cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface));
231
232
/*
233
* Get the misc control for PCS. We will need to set the
234
* duplication amount.
235
*/
236
pcsx_miscx_ctl_reg.u64 =
237
cvmx_read_csr(CVMX_PCSX_MISCX_CTL_REG(index, interface));
238
239
/*
240
* Use GMXENO to force the link down if the status we get says
241
* it should be down.
242
*/
243
pcsx_miscx_ctl_reg.s.gmxeno = !link_info.s.link_up;
244
245
/* Only change the duplex setting if the link is up */
246
if (link_info.s.link_up)
247
gmxx_prtx_cfg.s.duplex = link_info.s.full_duplex;
248
249
/* Do speed based setting for GMX */
250
switch (link_info.s.speed) {
251
case 10:
252
gmxx_prtx_cfg.s.speed = 0;
253
gmxx_prtx_cfg.s.speed_msb = 1;
254
gmxx_prtx_cfg.s.slottime = 0;
255
/* Setting from GMX-603 */
256
pcsx_miscx_ctl_reg.s.samp_pt = 25;
257
cvmx_write_csr(CVMX_GMXX_TXX_SLOT(index, interface), 64);
258
cvmx_write_csr(CVMX_GMXX_TXX_BURST(index, interface), 0);
259
break;
260
case 100:
261
gmxx_prtx_cfg.s.speed = 0;
262
gmxx_prtx_cfg.s.speed_msb = 0;
263
gmxx_prtx_cfg.s.slottime = 0;
264
pcsx_miscx_ctl_reg.s.samp_pt = 0x5;
265
cvmx_write_csr(CVMX_GMXX_TXX_SLOT(index, interface), 64);
266
cvmx_write_csr(CVMX_GMXX_TXX_BURST(index, interface), 0);
267
break;
268
case 1000:
269
gmxx_prtx_cfg.s.speed = 1;
270
gmxx_prtx_cfg.s.speed_msb = 0;
271
gmxx_prtx_cfg.s.slottime = 1;
272
pcsx_miscx_ctl_reg.s.samp_pt = 1;
273
cvmx_write_csr(CVMX_GMXX_TXX_SLOT(index, interface), 512);
274
cvmx_write_csr(CVMX_GMXX_TXX_BURST(index, interface), 8192);
275
break;
276
default:
277
break;
278
}
279
280
/* Write the new misc control for PCS */
281
cvmx_write_csr(CVMX_PCSX_MISCX_CTL_REG(index, interface),
282
pcsx_miscx_ctl_reg.u64);
283
284
/* Write the new GMX settings with the port still disabled */
285
cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), gmxx_prtx_cfg.u64);
286
287
/* Read GMX CFG again to make sure the config completed */
288
gmxx_prtx_cfg.u64 = cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface));
289
290
/* Restore the enabled / disabled state */
291
gmxx_prtx_cfg.s.en = is_enabled;
292
cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), gmxx_prtx_cfg.u64);
293
294
return 0;
295
}
296
297
/**
298
* Bring up the SGMII interface to be ready for packet I/O but
299
* leave I/O disabled using the GMX override. This function
300
* follows the bringup documented in 10.6.3 of the manual.
301
*
302
* @interface: Interface to bringup
303
* @num_ports: Number of ports on the interface
304
*
305
* Returns Zero on success, negative on failure
306
*/
307
static int __cvmx_helper_sgmii_hardware_init(int interface, int num_ports)
308
{
309
int index;
310
311
__cvmx_helper_setup_gmx(interface, num_ports);
312
313
for (index = 0; index < num_ports; index++) {
314
int ipd_port = cvmx_helper_get_ipd_port(interface, index);
315
__cvmx_helper_sgmii_hardware_init_one_time(interface, index);
316
/* Linux kernel driver will call ....link_set with the
317
* proper link state. In the simulator there is no
318
* link state polling and hence it is set from
319
* here.
320
*/
321
if (cvmx_sysinfo_get()->board_type == CVMX_BOARD_TYPE_SIM)
322
__cvmx_helper_sgmii_link_set(ipd_port,
323
__cvmx_helper_sgmii_link_get(ipd_port));
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
union cvmx_helper_link_info __cvmx_helper_sgmii_link_get(int ipd_port)
398
{
399
union cvmx_helper_link_info 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().
501
*
502
* @ipd_port: IPD/PKO port to configure
503
* @link_info: The new link state
504
*
505
* Returns Zero on success, negative on failure
506
*/
507
int __cvmx_helper_sgmii_link_set(int ipd_port,
508
union cvmx_helper_link_info link_info)
509
{
510
int interface = cvmx_helper_get_interface_num(ipd_port);
511
int index = cvmx_helper_get_interface_index_num(ipd_port);
512
__cvmx_helper_sgmii_hardware_init_link(interface, index);
513
return __cvmx_helper_sgmii_hardware_init_link_speed(interface, index,
514
link_info);
515
}
516
517