~funderscore blog cgit wiki get in touch
aboutsummaryrefslogtreecommitdiff
blob: 7eb99ac7c22bf9622959d023ce83eec2eb48f549 (plain)
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
// SPDX-License-Identifier: GPL-2.0
/*
 * Copyright (C) 2018-2022 Marvell International Ltd.
 *
 * Functions for AGL (RGMII) initialization, configuration,
 * and monitoring.
 */

#include <log.h>
#include <time.h>
#include <linux/delay.h>

#include <mach/cvmx-regs.h>
#include <mach/cvmx-csr.h>
#include <mach/cvmx-bootmem.h>
#include <mach/octeon-model.h>
#include <mach/cvmx-fuse.h>
#include <mach/octeon-feature.h>
#include <mach/cvmx-qlm.h>
#include <mach/octeon_qlm.h>
#include <mach/cvmx-pcie.h>
#include <mach/cvmx-coremask.h>

#include <mach/cvmx-helper.h>
#include <mach/cvmx-helper-cfg.h>

#include <mach/cvmx-agl.h>
#include <mach/cvmx-pki.h>

#include <mach/cvmx-agl-defs.h>
#include <mach/cvmx-pko-defs.h>

int __cvmx_helper_agl_enumerate(int xiface)
{
	if (OCTEON_IS_MODEL(OCTEON_CN70XX)) {
		union cvmx_agl_prtx_ctl agl_prtx_ctl;

		agl_prtx_ctl.u64 = csr_rd(CVMX_AGL_PRTX_CTL(0));
		if (agl_prtx_ctl.s.mode == 0) /* RGMII */
			return 1;
	}
	return 0;
}

/**
 * @INTERNAL
 * Convert interface to port to assess CSRs.
 *
 * @param xiface  Interface to probe
 * @return  The port corresponding to the interface
 */
int cvmx_helper_agl_get_port(int xiface)
{
	struct cvmx_xiface xi = cvmx_helper_xiface_to_node_interface(xiface);

	if (OCTEON_IS_MODEL(OCTEON_CN70XX))
		return xi.interface - 4;
	return -1;
}

/**
 * @INTERNAL
 * Probe a RGMII interface and determine the number of ports
 * connected to it. The RGMII interface should still be down
 * after this call.
 *
 * @param interface to probe
 *
 * @return Number of ports on the interface. Zero to disable.
 */
int __cvmx_helper_agl_probe(int interface)
{
	int port = cvmx_helper_agl_get_port(interface);
	union cvmx_agl_gmx_bist gmx_bist;
	union cvmx_agl_gmx_prtx_cfg gmx_prtx_cfg;
	union cvmx_agl_prtx_ctl agl_prtx_ctl;
	int result;

	result = __cvmx_helper_agl_enumerate(interface);
	if (result == 0)
		return 0;

	/* Check BIST status */
	gmx_bist.u64 = csr_rd(CVMX_AGL_GMX_BIST);
	if (gmx_bist.u64)
		printf("Management port AGL failed BIST (0x%016llx) on AGL%d\n",
		       CAST64(gmx_bist.u64), port);

	/* Disable the external input/output */
	gmx_prtx_cfg.u64 = csr_rd(CVMX_AGL_GMX_PRTX_CFG(port));
	gmx_prtx_cfg.s.en = 0;
	csr_wr(CVMX_AGL_GMX_PRTX_CFG(port), gmx_prtx_cfg.u64);

	/* Set the rgx_ref_clk MUX with AGL_PRTx_CTL[REFCLK_SEL]. Default value
	 * is 0 (RGMII REFCLK). Recommended to use RGMII RXC(1) or sclk/4 (2)
	 * to save cost.
	 */

	agl_prtx_ctl.u64 = csr_rd(CVMX_AGL_PRTX_CTL(port));
	agl_prtx_ctl.s.clkrst = 0;
	agl_prtx_ctl.s.dllrst = 0;
	agl_prtx_ctl.s.clktx_byp = 0;

	if (OCTEON_IS_MODEL(OCTEON_CN70XX)) {
		bool tx_enable_bypass;
		int tx_delay;

		agl_prtx_ctl.s.refclk_sel =
			cvmx_helper_get_agl_refclk_sel(interface, port);
		agl_prtx_ctl.s.clkrx_set =
			cvmx_helper_get_agl_rx_clock_skew(interface, port);
		agl_prtx_ctl.s.clkrx_byp =
			cvmx_helper_get_agl_rx_clock_delay_bypass(interface,
								  port);
		cvmx_helper_cfg_get_rgmii_tx_clk_delay(
			interface, port, &tx_enable_bypass, &tx_delay);
		agl_prtx_ctl.s.clktx_byp = tx_enable_bypass;
		agl_prtx_ctl.s.clktx_set = tx_delay;
	}
	csr_wr(CVMX_AGL_PRTX_CTL(port), agl_prtx_ctl.u64);
	/* Force write out before wait */
	csr_rd(CVMX_AGL_PRTX_CTL(port));
	udelay(500);

	/* Enable the componsation controller */
	agl_prtx_ctl.u64 = csr_rd(CVMX_AGL_PRTX_CTL(port));
	agl_prtx_ctl.s.drv_byp = 0;
	csr_wr(CVMX_AGL_PRTX_CTL(port), agl_prtx_ctl.u64);
	/* Force write out before wait */
	csr_rd(CVMX_AGL_PRTX_CTL(port));

	if (!OCTEON_IS_OCTEON3()) {
		/* Enable the interface */
		agl_prtx_ctl.u64 = csr_rd(CVMX_AGL_PRTX_CTL(port));
		agl_prtx_ctl.s.enable = 1;
		csr_wr(CVMX_AGL_PRTX_CTL(port), agl_prtx_ctl.u64);
		/* Read the value back to force the previous write */
		agl_prtx_ctl.u64 = csr_rd(CVMX_AGL_PRTX_CTL(port));
	}

	/* Enable the compensation controller */
	agl_prtx_ctl.u64 = csr_rd(CVMX_AGL_PRTX_CTL(port));
	agl_prtx_ctl.s.comp = 1;
	csr_wr(CVMX_AGL_PRTX_CTL(port), agl_prtx_ctl.u64);
	/* Force write out before wait */
	csr_rd(CVMX_AGL_PRTX_CTL(port));

	/* for componsation state to lock. */
	udelay(500);

	return result;
}

/**
 * @INTERNAL
 * Bringup and enable a RGMII interface. After this call packet
 * I/O should be fully functional. This is called with IPD
 * enabled but PKO disabled.
 *
 * @param interface to bring up
 *
 * @return Zero on success, negative on failure
 */
int __cvmx_helper_agl_enable(int interface)
{
	int port = cvmx_helper_agl_get_port(interface);
	int ipd_port = cvmx_helper_get_ipd_port(interface, port);
	union cvmx_pko_mem_port_ptrs pko_mem_port_ptrs;
	union cvmx_pko_reg_read_idx read_idx;
	int do_link_set = 1;
	int i;

	/* Setup PKO for AGL interface. Back pressure is not supported. */
	pko_mem_port_ptrs.u64 = 0;
	read_idx.u64 = 0;
	read_idx.s.inc = 1;
	csr_wr(CVMX_PKO_REG_READ_IDX, read_idx.u64);

	for (i = 0; i < 40; i++) {
		pko_mem_port_ptrs.u64 = csr_rd(CVMX_PKO_MEM_PORT_PTRS);
		if (pko_mem_port_ptrs.s.pid == 24) {
			pko_mem_port_ptrs.s.eid = 10;
			pko_mem_port_ptrs.s.bp_port = 40;
			csr_wr(CVMX_PKO_MEM_PORT_PTRS, pko_mem_port_ptrs.u64);
			break;
		}
	}

	cvmx_agl_enable(port);
	if (do_link_set)
		cvmx_agl_link_set(port, cvmx_agl_link_get(ipd_port));

	return 0;
}

/**
 * @INTERNAL
 * Return the link state of an IPD/PKO port as returned by
 * auto negotiation. The result of this function may not match
 * Octeon's link config if auto negotiation has changed since
 * the last call to cvmx_helper_link_set().
 *
 * @param ipd_port IPD/PKO port to query
 *
 * @return Link state
 */
cvmx_helper_link_info_t __cvmx_helper_agl_link_get(int ipd_port)
{
	return cvmx_agl_link_get(ipd_port);
}

/**
 * @INTERNAL
 * Configure an IPD/PKO port for the specified link state. This
 * function does not influence auto negotiation at the PHY level.
 * The passed link state must always match the link state returned
 * by cvmx_helper_link_get(). It is normally best to use
 * cvmx_helper_link_autoconf() instead.
 *
 * @param ipd_port  IPD/PKO port to configure
 * @param link_info The new link state
 *
 * @return Zero on success, negative on failure
 */
int __cvmx_helper_agl_link_set(int ipd_port, cvmx_helper_link_info_t link_info)
{
	int interface = cvmx_helper_get_interface_num(ipd_port);
	int port = cvmx_helper_agl_get_port(interface);

	return cvmx_agl_link_set(port, link_info);
}