diff options
Diffstat (limited to 'arch/mips/cavium-octeon/executive/cvmx-helper-xaui.c')
-rw-r--r-- | arch/mips/cavium-octeon/executive/cvmx-helper-xaui.c | 354 |
1 files changed, 354 insertions, 0 deletions
diff --git a/arch/mips/cavium-octeon/executive/cvmx-helper-xaui.c b/arch/mips/cavium-octeon/executive/cvmx-helper-xaui.c new file mode 100644 index 000000000000..1723248e987d --- /dev/null +++ b/arch/mips/cavium-octeon/executive/cvmx-helper-xaui.c | |||
@@ -0,0 +1,354 @@ | |||
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 XAUI initialization, configuration, | ||
30 | * and monitoring. | ||
31 | * | ||
32 | */ | ||
33 | |||
34 | #include <asm/octeon/octeon.h> | ||
35 | |||
36 | #include <asm/octeon/cvmx-config.h> | ||
37 | |||
38 | #include <asm/octeon/cvmx-helper.h> | ||
39 | |||
40 | #include <asm/octeon/cvmx-pko-defs.h> | ||
41 | #include <asm/octeon/cvmx-gmxx-defs.h> | ||
42 | #include <asm/octeon/cvmx-pcsxx-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 | int __cvmx_helper_xaui_enumerate(int interface) | ||
49 | { | ||
50 | union cvmx_gmxx_hg2_control gmx_hg2_control; | ||
51 | |||
52 | /* If HiGig2 is enabled return 16 ports, otherwise return 1 port */ | ||
53 | gmx_hg2_control.u64 = cvmx_read_csr(CVMX_GMXX_HG2_CONTROL(interface)); | ||
54 | if (gmx_hg2_control.s.hg2tx_en) | ||
55 | return 16; | ||
56 | else | ||
57 | return 1; | ||
58 | } | ||
59 | |||
60 | /** | ||
61 | * Probe a XAUI interface and determine the number of ports | ||
62 | * connected to it. The XAUI interface should still be down | ||
63 | * after this call. | ||
64 | * | ||
65 | * @interface: Interface to probe | ||
66 | * | ||
67 | * Returns Number of ports on the interface. Zero to disable. | ||
68 | */ | ||
69 | int __cvmx_helper_xaui_probe(int interface) | ||
70 | { | ||
71 | int i; | ||
72 | union cvmx_gmxx_inf_mode mode; | ||
73 | |||
74 | /* | ||
75 | * Due to errata GMX-700 on CN56XXp1.x and CN52XXp1.x, the | ||
76 | * interface needs to be enabled before IPD otherwise per port | ||
77 | * backpressure may not work properly. | ||
78 | */ | ||
79 | mode.u64 = cvmx_read_csr(CVMX_GMXX_INF_MODE(interface)); | ||
80 | mode.s.en = 1; | ||
81 | cvmx_write_csr(CVMX_GMXX_INF_MODE(interface), mode.u64); | ||
82 | |||
83 | __cvmx_helper_setup_gmx(interface, 1); | ||
84 | |||
85 | /* | ||
86 | * Setup PKO to support 16 ports for HiGig2 virtual | ||
87 | * ports. We're pointing all of the PKO packet ports for this | ||
88 | * interface to the XAUI. This allows us to use HiGig2 | ||
89 | * backpressure per port. | ||
90 | */ | ||
91 | for (i = 0; i < 16; i++) { | ||
92 | union cvmx_pko_mem_port_ptrs pko_mem_port_ptrs; | ||
93 | pko_mem_port_ptrs.u64 = 0; | ||
94 | /* | ||
95 | * We set each PKO port to have equal priority in a | ||
96 | * round robin fashion. | ||
97 | */ | ||
98 | pko_mem_port_ptrs.s.static_p = 0; | ||
99 | pko_mem_port_ptrs.s.qos_mask = 0xff; | ||
100 | /* All PKO ports map to the same XAUI hardware port */ | ||
101 | pko_mem_port_ptrs.s.eid = interface * 4; | ||
102 | pko_mem_port_ptrs.s.pid = interface * 16 + i; | ||
103 | cvmx_write_csr(CVMX_PKO_MEM_PORT_PTRS, pko_mem_port_ptrs.u64); | ||
104 | } | ||
105 | return __cvmx_helper_xaui_enumerate(interface); | ||
106 | } | ||
107 | |||
108 | /** | ||
109 | * Bringup and enable a XAUI interface. After this call packet | ||
110 | * I/O should be fully functional. This is called with IPD | ||
111 | * enabled but PKO disabled. | ||
112 | * | ||
113 | * @interface: Interface to bring up | ||
114 | * | ||
115 | * Returns Zero on success, negative on failure | ||
116 | */ | ||
117 | int __cvmx_helper_xaui_enable(int interface) | ||
118 | { | ||
119 | union cvmx_gmxx_prtx_cfg gmx_cfg; | ||
120 | union cvmx_pcsxx_control1_reg xauiCtl; | ||
121 | union cvmx_pcsxx_misc_ctl_reg xauiMiscCtl; | ||
122 | union cvmx_gmxx_tx_xaui_ctl gmxXauiTxCtl; | ||
123 | union cvmx_gmxx_rxx_int_en gmx_rx_int_en; | ||
124 | union cvmx_gmxx_tx_int_en gmx_tx_int_en; | ||
125 | union cvmx_pcsxx_int_en_reg pcsx_int_en_reg; | ||
126 | |||
127 | /* (1) Interface has already been enabled. */ | ||
128 | |||
129 | /* (2) Disable GMX. */ | ||
130 | xauiMiscCtl.u64 = cvmx_read_csr(CVMX_PCSXX_MISC_CTL_REG(interface)); | ||
131 | xauiMiscCtl.s.gmxeno = 1; | ||
132 | cvmx_write_csr(CVMX_PCSXX_MISC_CTL_REG(interface), xauiMiscCtl.u64); | ||
133 | |||
134 | /* (3) Disable GMX and PCSX interrupts. */ | ||
135 | gmx_rx_int_en.u64 = cvmx_read_csr(CVMX_GMXX_RXX_INT_EN(0, interface)); | ||
136 | cvmx_write_csr(CVMX_GMXX_RXX_INT_EN(0, interface), 0x0); | ||
137 | gmx_tx_int_en.u64 = cvmx_read_csr(CVMX_GMXX_TX_INT_EN(interface)); | ||
138 | cvmx_write_csr(CVMX_GMXX_TX_INT_EN(interface), 0x0); | ||
139 | pcsx_int_en_reg.u64 = cvmx_read_csr(CVMX_PCSXX_INT_EN_REG(interface)); | ||
140 | cvmx_write_csr(CVMX_PCSXX_INT_EN_REG(interface), 0x0); | ||
141 | |||
142 | /* (4) Bring up the PCSX and GMX reconciliation layer. */ | ||
143 | /* (4)a Set polarity and lane swapping. */ | ||
144 | /* (4)b */ | ||
145 | gmxXauiTxCtl.u64 = cvmx_read_csr(CVMX_GMXX_TX_XAUI_CTL(interface)); | ||
146 | /* Enable better IFG packing and improves performance */ | ||
147 | gmxXauiTxCtl.s.dic_en = 1; | ||
148 | gmxXauiTxCtl.s.uni_en = 0; | ||
149 | cvmx_write_csr(CVMX_GMXX_TX_XAUI_CTL(interface), gmxXauiTxCtl.u64); | ||
150 | |||
151 | /* (4)c Aply reset sequence */ | ||
152 | xauiCtl.u64 = cvmx_read_csr(CVMX_PCSXX_CONTROL1_REG(interface)); | ||
153 | xauiCtl.s.lo_pwr = 0; | ||
154 | xauiCtl.s.reset = 1; | ||
155 | cvmx_write_csr(CVMX_PCSXX_CONTROL1_REG(interface), xauiCtl.u64); | ||
156 | |||
157 | /* Wait for PCS to come out of reset */ | ||
158 | if (CVMX_WAIT_FOR_FIELD64 | ||
159 | (CVMX_PCSXX_CONTROL1_REG(interface), union cvmx_pcsxx_control1_reg, | ||
160 | reset, ==, 0, 10000)) | ||
161 | return -1; | ||
162 | /* Wait for PCS to be aligned */ | ||
163 | if (CVMX_WAIT_FOR_FIELD64 | ||
164 | (CVMX_PCSXX_10GBX_STATUS_REG(interface), | ||
165 | union cvmx_pcsxx_10gbx_status_reg, alignd, ==, 1, 10000)) | ||
166 | return -1; | ||
167 | /* Wait for RX to be ready */ | ||
168 | if (CVMX_WAIT_FOR_FIELD64 | ||
169 | (CVMX_GMXX_RX_XAUI_CTL(interface), union cvmx_gmxx_rx_xaui_ctl, | ||
170 | status, ==, 0, 10000)) | ||
171 | return -1; | ||
172 | |||
173 | /* (6) Configure GMX */ | ||
174 | gmx_cfg.u64 = cvmx_read_csr(CVMX_GMXX_PRTX_CFG(0, interface)); | ||
175 | gmx_cfg.s.en = 0; | ||
176 | cvmx_write_csr(CVMX_GMXX_PRTX_CFG(0, interface), gmx_cfg.u64); | ||
177 | |||
178 | /* Wait for GMX RX to be idle */ | ||
179 | if (CVMX_WAIT_FOR_FIELD64 | ||
180 | (CVMX_GMXX_PRTX_CFG(0, interface), union cvmx_gmxx_prtx_cfg, | ||
181 | rx_idle, ==, 1, 10000)) | ||
182 | return -1; | ||
183 | /* Wait for GMX TX to be idle */ | ||
184 | if (CVMX_WAIT_FOR_FIELD64 | ||
185 | (CVMX_GMXX_PRTX_CFG(0, interface), union cvmx_gmxx_prtx_cfg, | ||
186 | tx_idle, ==, 1, 10000)) | ||
187 | return -1; | ||
188 | |||
189 | /* GMX configure */ | ||
190 | gmx_cfg.u64 = cvmx_read_csr(CVMX_GMXX_PRTX_CFG(0, interface)); | ||
191 | gmx_cfg.s.speed = 1; | ||
192 | gmx_cfg.s.speed_msb = 0; | ||
193 | gmx_cfg.s.slottime = 1; | ||
194 | cvmx_write_csr(CVMX_GMXX_TX_PRTS(interface), 1); | ||
195 | cvmx_write_csr(CVMX_GMXX_TXX_SLOT(0, interface), 512); | ||
196 | cvmx_write_csr(CVMX_GMXX_TXX_BURST(0, interface), 8192); | ||
197 | cvmx_write_csr(CVMX_GMXX_PRTX_CFG(0, interface), gmx_cfg.u64); | ||
198 | |||
199 | /* (7) Clear out any error state */ | ||
200 | cvmx_write_csr(CVMX_GMXX_RXX_INT_REG(0, interface), | ||
201 | cvmx_read_csr(CVMX_GMXX_RXX_INT_REG(0, interface))); | ||
202 | cvmx_write_csr(CVMX_GMXX_TX_INT_REG(interface), | ||
203 | cvmx_read_csr(CVMX_GMXX_TX_INT_REG(interface))); | ||
204 | cvmx_write_csr(CVMX_PCSXX_INT_REG(interface), | ||
205 | cvmx_read_csr(CVMX_PCSXX_INT_REG(interface))); | ||
206 | |||
207 | /* Wait for receive link */ | ||
208 | if (CVMX_WAIT_FOR_FIELD64 | ||
209 | (CVMX_PCSXX_STATUS1_REG(interface), union cvmx_pcsxx_status1_reg, | ||
210 | rcv_lnk, ==, 1, 10000)) | ||
211 | return -1; | ||
212 | if (CVMX_WAIT_FOR_FIELD64 | ||
213 | (CVMX_PCSXX_STATUS2_REG(interface), union cvmx_pcsxx_status2_reg, | ||
214 | xmtflt, ==, 0, 10000)) | ||
215 | return -1; | ||
216 | if (CVMX_WAIT_FOR_FIELD64 | ||
217 | (CVMX_PCSXX_STATUS2_REG(interface), union cvmx_pcsxx_status2_reg, | ||
218 | rcvflt, ==, 0, 10000)) | ||
219 | return -1; | ||
220 | |||
221 | cvmx_write_csr(CVMX_GMXX_RXX_INT_EN(0, interface), gmx_rx_int_en.u64); | ||
222 | cvmx_write_csr(CVMX_GMXX_TX_INT_EN(interface), gmx_tx_int_en.u64); | ||
223 | cvmx_write_csr(CVMX_PCSXX_INT_EN_REG(interface), pcsx_int_en_reg.u64); | ||
224 | |||
225 | cvmx_helper_link_autoconf(cvmx_helper_get_ipd_port(interface, 0)); | ||
226 | |||
227 | /* (8) Enable packet reception */ | ||
228 | xauiMiscCtl.s.gmxeno = 0; | ||
229 | cvmx_write_csr(CVMX_PCSXX_MISC_CTL_REG(interface), xauiMiscCtl.u64); | ||
230 | |||
231 | gmx_cfg.u64 = cvmx_read_csr(CVMX_GMXX_PRTX_CFG(0, interface)); | ||
232 | gmx_cfg.s.en = 1; | ||
233 | cvmx_write_csr(CVMX_GMXX_PRTX_CFG(0, interface), gmx_cfg.u64); | ||
234 | |||
235 | __cvmx_interrupt_pcsx_intx_en_reg_enable(0, interface); | ||
236 | __cvmx_interrupt_pcsx_intx_en_reg_enable(1, interface); | ||
237 | __cvmx_interrupt_pcsx_intx_en_reg_enable(2, interface); | ||
238 | __cvmx_interrupt_pcsx_intx_en_reg_enable(3, interface); | ||
239 | __cvmx_interrupt_pcsxx_int_en_reg_enable(interface); | ||
240 | __cvmx_interrupt_gmxx_enable(interface); | ||
241 | |||
242 | return 0; | ||
243 | } | ||
244 | |||
245 | /** | ||
246 | * Return the link state of an IPD/PKO port as returned by | ||
247 | * auto negotiation. The result of this function may not match | ||
248 | * Octeon's link config if auto negotiation has changed since | ||
249 | * the last call to cvmx_helper_link_set(). | ||
250 | * | ||
251 | * @ipd_port: IPD/PKO port to query | ||
252 | * | ||
253 | * Returns Link state | ||
254 | */ | ||
255 | cvmx_helper_link_info_t __cvmx_helper_xaui_link_get(int ipd_port) | ||
256 | { | ||
257 | int interface = cvmx_helper_get_interface_num(ipd_port); | ||
258 | union cvmx_gmxx_tx_xaui_ctl gmxx_tx_xaui_ctl; | ||
259 | union cvmx_gmxx_rx_xaui_ctl gmxx_rx_xaui_ctl; | ||
260 | union cvmx_pcsxx_status1_reg pcsxx_status1_reg; | ||
261 | cvmx_helper_link_info_t result; | ||
262 | |||
263 | gmxx_tx_xaui_ctl.u64 = cvmx_read_csr(CVMX_GMXX_TX_XAUI_CTL(interface)); | ||
264 | gmxx_rx_xaui_ctl.u64 = cvmx_read_csr(CVMX_GMXX_RX_XAUI_CTL(interface)); | ||
265 | pcsxx_status1_reg.u64 = | ||
266 | cvmx_read_csr(CVMX_PCSXX_STATUS1_REG(interface)); | ||
267 | result.u64 = 0; | ||
268 | |||
269 | /* Only return a link if both RX and TX are happy */ | ||
270 | if ((gmxx_tx_xaui_ctl.s.ls == 0) && (gmxx_rx_xaui_ctl.s.status == 0) && | ||
271 | (pcsxx_status1_reg.s.rcv_lnk == 1)) { | ||
272 | result.s.link_up = 1; | ||
273 | result.s.full_duplex = 1; | ||
274 | result.s.speed = 10000; | ||
275 | } else { | ||
276 | /* Disable GMX and PCSX interrupts. */ | ||
277 | cvmx_write_csr(CVMX_GMXX_RXX_INT_EN(0, interface), 0x0); | ||
278 | cvmx_write_csr(CVMX_GMXX_TX_INT_EN(interface), 0x0); | ||
279 | cvmx_write_csr(CVMX_PCSXX_INT_EN_REG(interface), 0x0); | ||
280 | } | ||
281 | return result; | ||
282 | } | ||
283 | |||
284 | /** | ||
285 | * Configure an IPD/PKO port for the specified link state. This | ||
286 | * function does not influence auto negotiation at the PHY level. | ||
287 | * The passed link state must always match the link state returned | ||
288 | * by cvmx_helper_link_get(). It is normally best to use | ||
289 | * cvmx_helper_link_autoconf() instead. | ||
290 | * | ||
291 | * @ipd_port: IPD/PKO port to configure | ||
292 | * @link_info: The new link state | ||
293 | * | ||
294 | * Returns Zero on success, negative on failure | ||
295 | */ | ||
296 | int __cvmx_helper_xaui_link_set(int ipd_port, cvmx_helper_link_info_t link_info) | ||
297 | { | ||
298 | int interface = cvmx_helper_get_interface_num(ipd_port); | ||
299 | union cvmx_gmxx_tx_xaui_ctl gmxx_tx_xaui_ctl; | ||
300 | union cvmx_gmxx_rx_xaui_ctl gmxx_rx_xaui_ctl; | ||
301 | |||
302 | gmxx_tx_xaui_ctl.u64 = cvmx_read_csr(CVMX_GMXX_TX_XAUI_CTL(interface)); | ||
303 | gmxx_rx_xaui_ctl.u64 = cvmx_read_csr(CVMX_GMXX_RX_XAUI_CTL(interface)); | ||
304 | |||
305 | /* If the link shouldn't be up, then just return */ | ||
306 | if (!link_info.s.link_up) | ||
307 | return 0; | ||
308 | |||
309 | /* Do nothing if both RX and TX are happy */ | ||
310 | if ((gmxx_tx_xaui_ctl.s.ls == 0) && (gmxx_rx_xaui_ctl.s.status == 0)) | ||
311 | return 0; | ||
312 | |||
313 | /* Bring the link up */ | ||
314 | return __cvmx_helper_xaui_enable(interface); | ||
315 | } | ||
316 | |||
317 | /** | ||
318 | * Configure a port for internal and/or external loopback. Internal loopback | ||
319 | * causes packets sent by the port to be received by Octeon. External loopback | ||
320 | * causes packets received from the wire to sent out again. | ||
321 | * | ||
322 | * @ipd_port: IPD/PKO port to loopback. | ||
323 | * @enable_internal: | ||
324 | * Non zero if you want internal loopback | ||
325 | * @enable_external: | ||
326 | * Non zero if you want external loopback | ||
327 | * | ||
328 | * Returns Zero on success, negative on failure. | ||
329 | */ | ||
330 | extern int __cvmx_helper_xaui_configure_loopback(int ipd_port, | ||
331 | int enable_internal, | ||
332 | int enable_external) | ||
333 | { | ||
334 | int interface = cvmx_helper_get_interface_num(ipd_port); | ||
335 | union cvmx_pcsxx_control1_reg pcsxx_control1_reg; | ||
336 | union cvmx_gmxx_xaui_ext_loopback gmxx_xaui_ext_loopback; | ||
337 | |||
338 | /* Set the internal loop */ | ||
339 | pcsxx_control1_reg.u64 = | ||
340 | cvmx_read_csr(CVMX_PCSXX_CONTROL1_REG(interface)); | ||
341 | pcsxx_control1_reg.s.loopbck1 = enable_internal; | ||
342 | cvmx_write_csr(CVMX_PCSXX_CONTROL1_REG(interface), | ||
343 | pcsxx_control1_reg.u64); | ||
344 | |||
345 | /* Set the external loop */ | ||
346 | gmxx_xaui_ext_loopback.u64 = | ||
347 | cvmx_read_csr(CVMX_GMXX_XAUI_EXT_LOOPBACK(interface)); | ||
348 | gmxx_xaui_ext_loopback.s.en = enable_external; | ||
349 | cvmx_write_csr(CVMX_GMXX_XAUI_EXT_LOOPBACK(interface), | ||
350 | gmxx_xaui_ext_loopback.u64); | ||
351 | |||
352 | /* Take the link through a reset */ | ||
353 | return __cvmx_helper_xaui_enable(interface); | ||
354 | } | ||