1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
|
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2021-2022 Stefan Roese <sr@denx.de>
*/
#include <cyclic.h>
#include <dm.h>
#include <ram.h>
#include <time.h>
#include <asm/gpio.h>
#include <mach/octeon_ddr.h>
#include <mach/cvmx-qlm.h>
#include <mach/octeon_qlm.h>
#include <mach/octeon_fdt.h>
#include <mach/cvmx-helper.h>
#include <mach/cvmx-helper-cfg.h>
#include <mach/cvmx-helper-util.h>
#include <mach/cvmx-bgxx-defs.h>
#include <mach/cvmx-dtx-defs.h>
#include "board_ddr.h"
/**
* cvmx_spem#_cfg_rd
*
* This register allows read access to the configuration in the PCIe core.
*
*/
union cvmx_spemx_cfg_rd {
u64 u64;
struct cvmx_spemx_cfg_rd_s {
u64 data : 32;
u64 addr : 32;
} s;
struct cvmx_spemx_cfg_rd_s cn73xx;
};
/**
* cvmx_spem#_cfg_wr
*
* This register allows write access to the configuration in the PCIe core.
*
*/
union cvmx_spemx_cfg_wr {
u64 u64;
struct cvmx_spemx_cfg_wr_s {
u64 data : 32;
u64 addr : 32;
} s;
struct cvmx_spemx_cfg_wr_s cn73xx;
};
/**
* cvmx_spem#_flr_pf_stopreq
*
* PF function level reset stop outbound requests register.
* Hardware automatically sets the STOPREQ bit for the PF when it enters a
* function level reset (FLR). Software is responsible for clearing the STOPREQ
* bit but must not do so prior to hardware taking down the FLR, which could be
* as long as 100ms. It may be appropriate for software to wait longer before clearing
* STOPREQ, software may need to drain deep DPI queues for example.
* Whenever SPEM receives a PF or child VF request mastered by CNXXXX over S2M (i.e. P or NP),
* when STOPREQ is set for the function, SPEM will discard the outgoing request
* before sending it to the PCIe core. If a NP, SPEM will schedule an immediate
* SWI_RSP_ERROR completion for the request - no timeout is required.
* In both cases, SPEM()_DBG_PF()_INFO[P()_BMD_E] will be set and a error
* interrupt is generated.
*
* STOPREQ mimics the behavior of PCIEEP()_CFG001[ME] for outbound requests that will
* master the PCIe bus (P and NP).
*
* STOPREQ will have no effect on completions returned by CNXXXX over the S2M,
* nor on M2S traffic.
*
* When a PF()_STOPREQ is set, none of the associated
* PEM()_FLR_PF()_VF_STOPREQ[VF_STOPREQ] will be set.
*
* STOPREQ is reset when the MAC is reset, and is not reset after a chip soft reset.
*/
union cvmx_spemx_flr_pf_stopreq {
u64 u64;
struct cvmx_spemx_flr_pf_stopreq_s {
u64 reserved_3_63 : 61;
u64 pf2_stopreq : 1;
u64 pf1_stopreq : 1;
u64 pf0_stopreq : 1;
} s;
struct cvmx_spemx_flr_pf_stopreq_s cn73xx;
};
#define CVMX_SPEMX_CFG_WR(offset) 0x00011800C0000028ull
#define CVMX_SPEMX_CFG_RD(offset) 0x00011800C0000030ull
#define CVMX_SPEMX_FLR_PF_STOPREQ(offset) 0x00011800C0000218ull
#define DTX_SELECT_LTSSM 0x0
#define DTX_SELECT_LTSSM_ENA 0x3ff
#define LTSSM_L0 0x11
#define NIC23_DEF_DRAM_FREQ 800
static u32 pci_cfgspace_reg0[2] = { 0, 0 };
static u8 octeon_nic23_cfg0_spd_values[512] = {
OCTEON_NIC23_CFG0_SPD_VALUES
};
static struct ddr_conf board_ddr_conf[] = {
OCTEON_NIC23_DDR_CONFIGURATION
};
struct ddr_conf *octeon_ddr_conf_table_get(int *count, int *def_ddr_freq)
{
*count = ARRAY_SIZE(board_ddr_conf);
*def_ddr_freq = NIC23_DEF_DRAM_FREQ;
return board_ddr_conf;
}
int board_fix_fdt(void *fdt)
{
u32 range_data[5 * 8];
bool rev4;
int node;
int rc;
/*
* ToDo:
* Read rev4 info from EEPROM or where the original U-Boot does
* and don't hard-code it here.
*/
rev4 = true;
debug("%s() rev4: %s\n", __func__, rev4 ? "true" : "false");
/* Patch the PHY configuration based on board revision */
rc = octeon_fdt_patch_rename(fdt,
rev4 ? "4,nor-flash" : "4,no-nor-flash",
"cavium,board-trim", false, NULL, NULL);
if (!rev4) {
/* Modify the ranges for CS 0 */
node = fdt_node_offset_by_compatible(fdt, -1,
"cavium,octeon-3860-bootbus");
if (node < 0) {
printf("%s: Error: cannot find boot bus in device tree!\n",
__func__);
return -1;
}
rc = fdtdec_get_int_array(fdt, node, "ranges",
range_data, 5 * 8);
if (rc) {
printf("%s: Error reading ranges from boot bus FDT\n",
__func__);
return -1;
}
range_data[2] = cpu_to_fdt32(0x10000);
range_data[3] = 0;
range_data[4] = 0;
rc = fdt_setprop(fdt, node, "ranges", range_data,
sizeof(range_data));
if (rc) {
printf("%s: Error updating boot bus ranges in fdt\n",
__func__);
}
}
return rc;
}
int board_early_init_f(void)
{
struct gpio_desc gpio = {};
ofnode node;
/* Initial GPIO configuration */
/* GPIO 7: Vitesse reset */
node = ofnode_by_compatible(ofnode_null(), "vitesse,vsc7224");
if (ofnode_valid(node)) {
gpio_request_by_name_nodev(node, "los", 0, &gpio, GPIOD_IS_IN);
dm_gpio_free(gpio.dev, &gpio);
gpio_request_by_name_nodev(node, "reset", 0, &gpio,
GPIOD_IS_OUT);
if (dm_gpio_is_valid(&gpio)) {
/* Vitesse reset */
debug("%s: Setting GPIO 7 to 1\n", __func__);
dm_gpio_set_value(&gpio, 1);
}
dm_gpio_free(gpio.dev, &gpio);
}
/* SFP+ transmitters */
ofnode_for_each_compatible_node(node, "ethernet,sfp-slot") {
gpio_request_by_name_nodev(node, "tx_disable", 0,
&gpio, GPIOD_IS_OUT);
if (dm_gpio_is_valid(&gpio)) {
debug("%s: Setting GPIO %d to 1\n", __func__,
gpio.offset);
dm_gpio_set_value(&gpio, 1);
}
dm_gpio_free(gpio.dev, &gpio);
gpio_request_by_name_nodev(node, "mod_abs", 0, &gpio,
GPIOD_IS_IN);
dm_gpio_free(gpio.dev, &gpio);
gpio_request_by_name_nodev(node, "tx_error", 0, &gpio,
GPIOD_IS_IN);
dm_gpio_free(gpio.dev, &gpio);
gpio_request_by_name_nodev(node, "rx_los", 0, &gpio,
GPIOD_IS_IN);
dm_gpio_free(gpio.dev, &gpio);
}
return 0;
}
void board_configure_qlms(void)
{
octeon_configure_qlm(4, 3000, CVMX_QLM_MODE_SATA_2X1, 0, 0, 0, 0);
octeon_configure_qlm(5, 103125, CVMX_QLM_MODE_XFI_1X2, 0, 0, 2, 0);
/* Apply amplitude tuning to 10G interface */
octeon_qlm_tune_v3(0, 4, 3000, -1, -1, 7, -1);
octeon_qlm_tune_v3(0, 5, 103125, 0x19, 0x0, -1, -1);
octeon_qlm_set_channel_v3(0, 5, 0);
octeon_qlm_dfe_disable(0, 5, -1, 103125, CVMX_QLM_MODE_XFI_1X2);
debug("QLM 4 reference clock: %d\n"
"DLM 5 reference clock: %d\n",
cvmx_qlm_measure_clock(4), cvmx_qlm_measure_clock(5));
}
/**
* If there is a PF FLR then the PCI EEPROM is not re-read. In this case
* we need to re-program the vendor and device ID immediately after hardware
* completes FLR.
*
* PCI spec requires FLR to be completed within 100ms. The user who triggered
* FLR expects hardware to finish FLR within 100ms, otherwise the user will
* end up reading DEVICE_ID incorrectly from the reset value.
* CN23XX exits FLR at any point between 66 and 99ms, so U-Boot has to wait
* 99ms to let hardware finish its part, then finish reprogramming the
* correct device ID before the end of 100ms.
*
* Note: this solution only works properly when there is no other activity
* within U-Boot for 100ms from the time FLR is triggered.
*
* This function gets called every 100usec. If FLR happens during any
* other activity like bootloader/image update then it is possible that
* this function does not get called for more than the FLR period which will
* cause the PF device ID restore to happen after whoever initiated the FLR to
* read the incorrect device ID 0x9700 (reset value) instead of 0x9702
* (restored value).
*/
static void octeon_board_restore_pf(void *ctx)
{
union cvmx_spemx_flr_pf_stopreq stopreq;
static bool start_initialized[2] = {false, false};
bool pf0_flag, pf1_flag;
u64 ltssm_bits;
const u64 pf_flr_wait_usecs = 99700;
u64 elapsed_usecs;
union cvmx_spemx_cfg_wr cfg_wr;
union cvmx_spemx_cfg_rd cfg_rd;
static u64 start_us[2];
int pf_num;
csr_wr(CVMX_DTX_SPEM_SELX(0), DTX_SELECT_LTSSM);
csr_rd(CVMX_DTX_SPEM_SELX(0));
csr_wr(CVMX_DTX_SPEM_ENAX(0), DTX_SELECT_LTSSM_ENA);
csr_rd(CVMX_DTX_SPEM_ENAX(0));
ltssm_bits = csr_rd(CVMX_DTX_SPEM_DATX(0));
if (((ltssm_bits >> 3) & 0x3f) != LTSSM_L0)
return;
stopreq.u64 = csr_rd(CVMX_SPEMX_FLR_PF_STOPREQ(0));
pf0_flag = stopreq.s.pf0_stopreq;
pf1_flag = stopreq.s.pf1_stopreq;
/* See if PF interrupt happened */
if (!(pf0_flag || pf1_flag))
return;
if (pf0_flag && !start_initialized[0]) {
start_initialized[0] = true;
start_us[0] = get_timer_us(0);
}
/* Store programmed PCIe DevID SPEM0 PF0 */
if (pf0_flag && !pci_cfgspace_reg0[0]) {
cfg_rd.s.addr = (0 << 24) | 0x0;
csr_wr(CVMX_SPEMX_CFG_RD(0), cfg_rd.u64);
cfg_rd.u64 = csr_rd(CVMX_SPEMX_CFG_RD(0));
pci_cfgspace_reg0[0] = cfg_rd.s.data;
}
if (pf1_flag && !start_initialized[1]) {
start_initialized[1] = true;
start_us[1] = get_timer_us(0);
}
/* Store programmed PCIe DevID SPEM0 PF1 */
if (pf1_flag && !pci_cfgspace_reg0[1]) {
cfg_rd.s.addr = (1 << 24) | 0x0;
csr_wr(CVMX_SPEMX_CFG_RD(0), cfg_rd.u64);
cfg_rd.u64 = csr_rd(CVMX_SPEMX_CFG_RD(0));
pci_cfgspace_reg0[1] = cfg_rd.s.data;
}
/* For PF, rewrite pci config space reg 0 */
for (pf_num = 0; pf_num < 2; pf_num++) {
if (!start_initialized[pf_num])
continue;
elapsed_usecs = get_timer_us(0) - start_us[pf_num];
if (elapsed_usecs > pf_flr_wait_usecs) {
/* Here, our measured FLR duration has passed;
* check if device ID has been reset,
* which indicates FLR completion (per MA team).
*/
cfg_rd.s.addr = (pf_num << 24) | 0x0;
csr_wr(CVMX_SPEMX_CFG_RD(0), cfg_rd.u64);
cfg_rd.u64 = csr_rd(CVMX_SPEMX_CFG_RD(0));
/* if DevID has NOT been reset, FLR is not yet
* complete
*/
if (cfg_rd.s.data != pci_cfgspace_reg0[pf_num]) {
stopreq.s.pf0_stopreq = (pf_num == 0) ? 1 : 0;
stopreq.s.pf1_stopreq = (pf_num == 1) ? 1 : 0;
csr_wr(CVMX_SPEMX_FLR_PF_STOPREQ(0), stopreq.u64);
cfg_wr.u64 = 0;
cfg_wr.s.addr = (pf_num << 24) | 0;
cfg_wr.s.data = pci_cfgspace_reg0[pf_num];
csr_wr(CVMX_SPEMX_CFG_WR(0), cfg_wr.u64);
start_initialized[pf_num] = false;
}
}
}
}
int board_late_init(void)
{
struct cyclic_info *cyclic;
struct gpio_desc gpio = {};
ofnode node;
/* Turn on SFP+ transmitters */
ofnode_for_each_compatible_node(node, "ethernet,sfp-slot") {
gpio_request_by_name_nodev(node, "tx_disable", 0,
&gpio, GPIOD_IS_OUT);
if (dm_gpio_is_valid(&gpio)) {
debug("%s: Setting GPIO %d to 0\n", __func__,
gpio.offset);
dm_gpio_set_value(&gpio, 0);
}
dm_gpio_free(gpio.dev, &gpio);
}
board_configure_qlms();
/* Register cyclic function for PCIe FLR fixup */
cyclic = cyclic_register(octeon_board_restore_pf, 100,
"pcie_flr_fix", NULL);
if (!cyclic)
printf("Registering of cyclic function failed\n");
return 0;
}
int last_stage_init(void)
{
struct gpio_desc gpio = {};
ofnode node;
node = ofnode_by_compatible(ofnode_null(), "vitesse,vsc7224");
if (!ofnode_valid(node)) {
printf("Vitesse SPF DT node not found!");
return 0;
}
gpio_request_by_name_nodev(node, "reset", 0, &gpio, GPIOD_IS_OUT);
if (dm_gpio_is_valid(&gpio)) {
/* Take Vitesse retimer out of reset */
debug("%s: Setting GPIO 7 to 0\n", __func__);
dm_gpio_set_value(&gpio, 0);
mdelay(50);
}
dm_gpio_free(gpio.dev, &gpio);
return 0;
}
|