Commit | Line | Data |
---|---|---|
920dae64 AT |
1 | \ ========== Copyright Header Begin ========================================== |
2 | \ | |
3 | \ Hypervisor Software File: core.fth | |
4 | \ | |
5 | \ Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved. | |
6 | \ | |
7 | \ - Do no alter or remove copyright notices | |
8 | \ | |
9 | \ - Redistribution and use of this software in source and binary forms, with | |
10 | \ or without modification, are permitted provided that the following | |
11 | \ conditions are met: | |
12 | \ | |
13 | \ - Redistribution of source code must retain the above copyright notice, | |
14 | \ this list of conditions and the following disclaimer. | |
15 | \ | |
16 | \ - Redistribution in binary form must reproduce the above copyright notice, | |
17 | \ this list of conditions and the following disclaimer in the | |
18 | \ documentation and/or other materials provided with the distribution. | |
19 | \ | |
20 | \ Neither the name of Sun Microsystems, Inc. or the names of contributors | |
21 | \ may be used to endorse or promote products derived from this software | |
22 | \ without specific prior written permission. | |
23 | \ | |
24 | \ This software is provided "AS IS," without a warranty of any kind. | |
25 | \ ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, | |
26 | \ INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A | |
27 | \ PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN | |
28 | \ MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR | |
29 | \ ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR | |
30 | \ DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN | |
31 | \ OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR | |
32 | \ FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE | |
33 | \ DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, | |
34 | \ ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF | |
35 | \ SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. | |
36 | \ | |
37 | \ You acknowledge that this software is not designed, licensed or | |
38 | \ intended for use in the design, construction, operation or maintenance of | |
39 | \ any nuclear facility. | |
40 | \ | |
41 | \ ========== Copyright Header End ============================================ | |
42 | id: @(#)core.fth 1.2 07/03/13 | |
43 | purpose: | |
44 | copyright: Copyright 2007 Sun Microsystems, Inc. All rights reserved. | |
45 | copyright: Use is subject to license terms. | |
46 | ||
47 | headerless | |
48 | ||
49 | \ Possible vables of PKTBUFSZ field of the Receive Completion Ring Entry | |
50 | b# 00 constant bufsz0 | |
51 | b# 01 constant bufsz1 | |
52 | b# 10 constant bufsz2 | |
53 | b# 11 constant singleblk | |
54 | ||
55 | \ Neptune Tx packet descriptor structure. Flags occupies bits [63:44] | |
56 | h# 1 d# 63 lshift constant sof \ Start of Frame | |
57 | h# 1 d# 62 lshift constant mark \ Interrupt after pkt is transmitted | |
58 | h# 1 d# 58 lshift constant num-ptr=1 \ # of gather pointers of this pkt=1 | |
59 | h# 1fff d# 44 lshift constant tr-len-mask \ Mask for TR_LEN (bits[56:44]) | |
60 | ||
61 | \ SW must toggle this bit every time the tail wraps around the ring. | |
62 | h# 8.0000 constant wrap=1 | |
63 | 0 constant wrap=0 | |
64 | wrap=0 value wrap | |
65 | ||
66 | 0 value last-rmd-idx | |
67 | ||
68 | \ Get the HEAD field of register TX_RING_HDL to check which descriptor | |
69 | \ has been processed by the hardware | |
70 | \ | |
71 | \ In Neptune, software appends packets to be transmitted to the tail | |
72 | \ of the TX descriptor ring; HW hammers the packet at the head of | |
73 | \ the TX ring to the medium. When we transmit one packet at a time, | |
74 | \ the head will follow the tail! Packets have been sent out when | |
75 | \ head matches tail. | |
76 | \ | |
77 | : tx-head@ ( chan -- head ) tx-ring-hdl-i@ 3 xrshift h# ffff and ; | |
78 | : tx-hdr-wrap@ ( chan -- wrap ) tx-ring-hdl-i@ d# 44 lshift d# 63 xrshift ; | |
79 | : tx-tail@ ( chan -- tail ) tx-ring-kick-i@ 3 xrshift h# ffff and ; | |
80 | : tx-tail-wrap@ ( chan -- wrap ) tx-ring-kick-i@ d# 44 lshift d# 63 xrshift ; | |
81 | ||
82 | false instance value restart? \ To flag serious errors | |
83 | ||
84 | \ Words to access the 64-bit message descriptors. | |
85 | \ xbflip takes care of SPARC's big endianess | |
86 | \ | |
87 | : descriptor64@ ( addr -- x ) x@ xbflip ; | |
88 | : descriptor64! ( x addr -- ) >r xbflip r> x! ; | |
89 | ||
90 | : descriptor32@ ( addr -- x ) l@ lbflip ; | |
91 | : descriptor32! ( x addr -- ) >r lbflip r> l! ; | |
92 | ||
93 | \ RX descriptor ring address calculations | |
94 | : rmd#>rmdaddr ( n -- addr ) /rmd * rmd0 + ; | |
95 | : rmdaddr>rmd# ( addr -- n ) rmd0 - /rmd / ; | |
96 | ||
97 | \ TX descriptor ring address calculations | |
98 | \ /tmd=8 (bytes), which is the size of tx descriptor | |
99 | : tmd#>tmdaddr ( n -- addr ) #tmds mod /tmd * tmd0 + ; | |
100 | : tmdaddr>tmd# ( addr -- n ) tmd0 - /tmd / #tmds mod ; | |
101 | ||
102 | \ RX completion ring address calculations | |
103 | : rcd#>rcdaddr ( n -- addr ) #rcds mod /rcd * rcd0 + ; | |
104 | : rcdaddr>rcd# ( addr -- n ) rcd0 - /rcd / #rcds mod ; | |
105 | ||
106 | \ RX buffer address calculations | |
107 | \ #rbufs is the total number of Rx buffers = 60 = 64-4 | |
108 | \ /rbuf is the size of Rx data block, 8K each | |
109 | : rbuf#>rbuf-io-adr ( n -- addr ) #rbufs mod /rbuf * io-rbuf0 + ; | |
110 | ||
111 | \ Convert rcd-cpu-addr to rcd-io-addr | |
112 | : rcd-cpu-addr>rcd-io-addr ( rcd-cpu-addr -- rcd-io-addr ) | |
113 | rcd0 - io-rcd0 + | |
114 | ; | |
115 | ||
116 | : tmdheader@ ( tmdaddr -- len ) | |
117 | descriptor64@ | |
118 | d# 44 xrshift d# 44 lshift \ Clear the lower 44 bits of | |
119 | ; \ descriptor contents | |
120 | ||
121 | \ Pointer to the shared 4K Tx data buffer is the lower 44 bits | |
122 | \ of the descriptor. This word gets the contents of the descriptor | |
123 | \ then mask off the top 20 bits of the 64 bits descriptor. | |
124 | \ | |
125 | : txbufptr@ ( tmdaddr -- addr ) | |
126 | descriptor64@ | |
127 | d# 20 lshift d# 20 xrshift | |
128 | ; | |
129 | ||
130 | \ Get current tmd entry, clear bits 63:44, OR the addr of | |
131 | \ block buffer with new flags including len. Must use xrshift | |
132 | \ to avoid truncating. | |
133 | \ | |
134 | : tmdheader! ( hdr tmdaddr -- ) | |
135 | dup ( hdr tmdaddr tmdaddr ) | |
136 | txbufptr@ ( hdr tmdaddr low44_cur_tmd ) | |
137 | rot ( tmdaddr low44_cur_tmd hdr ) | |
138 | or ( tmdaddr lenORlow44_cur_tmd ) | |
139 | swap ( new_tmd tmdaddr ) \ new_tmd = len OR low44bits_cur_tmd | |
140 | descriptor64! | |
141 | ; | |
142 | ||
143 | \ The TX descriptor has two parts, the leading 20 bits are flags and | |
144 | \ the length of data, the remaining 44 bits are address of the data | |
145 | \ buffer. This word only puts the address of a data buffer to the | |
146 | \ lower 44 bits of the descriptor without changing the leadring | |
147 | \ 20 bits, which are set by tmdheader! | |
148 | \ | |
149 | : txbufptr! ( bufptr tmdaddr -- ) | |
150 | >r ( bufptr ) ( r: tmdaddr ) | |
151 | d# 20 lshift d# 20 xrshift \ Clear the top 20 bits of bufptr | |
152 | r> ( low44_bufptr tmdaddr ) | |
153 | dup ( low44_bufptr tmdaddr tmdaddr ) | |
154 | tmdheader@ ( low44_bufptr tmdaddr top20bits_cur_tmd ) | |
155 | rot ( tmdaddr top20bits_cur_tmd low44_bufptr ) | |
156 | or ( tmdaddr top20bits_cur_tmd-OR-low44_bufptr ) | |
157 | swap ( new_tmd tmdaddr ) \ new_tmd=top20bits_cur_tmd OR low44_bufptr | |
158 | descriptor64! | |
159 | ; | |
160 | ||
161 | \ RX descriptor fields | |
162 | \ | |
163 | : rxbufptr! ( bufptr rmdaddr -- ) | |
164 | swap ( rmdaddr bufptr ) | |
165 | d# 12 xrshift d# 12 lshift \ Clear the low 12 bits | |
166 | swap | |
167 | descriptor64! | |
168 | ; | |
169 | ||
170 | : rxbufptr@ ( rmdaddr -- bufptr ) | |
171 | descriptor64@ | |
172 | \ What we read from the descriptor is bits[43:12] of addr | |
173 | d# 12 lshift \ now append 12 0s to bits[43:12] | |
174 | ; | |
175 | ||
176 | : synciopb ( cpu-addr size -- ) | |
177 | over ( cpu-addr size cpu-addr ) | |
178 | cpu>io-adr ( cpu-addr size dev-addr ) | |
179 | swap ( cpu-addr io-addr size ) | |
180 | dma-sync ( ) | |
181 | ; | |
182 | ||
183 | \ Set logic device group 0 to include Channel0 RxDMA (logic devie 0), | |
184 | \ Channel0 TxDMA (logic device 32) and port-th MAC (logic device 64,65,66,67). | |
185 | \ Set logic device group 63 for Device Error (logic device 68) | |
186 | \ | |
187 | \ Notice that these registers are blocked off in NIU and must be accessed | |
188 | \ via Hypervisor APIs. | |
189 | \ | |
190 | 0 constant ld-rx-dma0 | |
191 | d# 32 constant ld-tx-dma0 | |
192 | d# 64 constant ld-mac0 | |
193 | d# 68 constant ld-sys-err | |
194 | ||
195 | : set-ldg ( -- ) | |
196 | ld-rx-dma0 ldg0 ldg-num-i! | |
197 | ld-tx-dma0 ldg0 ldg-num-i! | |
198 | ld-mac0 port + ldg0 ldg-num-i! | |
199 | ld-sys-err ldg63 ldg-num-i! | |
200 | ; | |
201 | ||
202 | ||
203 | \ RX descriptor Initialization | |
204 | \ rbuf-io-adr: io addr of a 8K data block buffer for hw to use. | |
205 | \ rmd-cpu-adr: cpu addr of a Rx descriptor entry for driver to use. | |
206 | \ Before lshift | |
207 | \ 63 43 0 | |
208 | \ +-----------+------------------------+ | |
209 | \ |xxxxxxxxxxx| high addr low-adr | | |
210 | \ +-----------+------------------------+ | |
211 | \ After lshift 20 then xrshift 32 | |
212 | \ 63 31 0 | |
213 | \ +------------------+-----------------+ | |
214 | \ |000000000000000000| high addr |000low-adr00 | |
215 | \ +------------------+-----------------+ | |
216 | \ 43 12 0 | |
217 | \ After this word is called, we should see initialized rx | |
218 | \ descriptors at cpu memory location rmd-cpu-adr | |
219 | \ which is the result of | |
220 | \ i rmd#>rmdaddr | |
221 | \ where rmd#>rmdaddr ( n -- addr ) is defined as | |
222 | \ /rmd * rmd0 + ; | |
223 | \ Do dump rmd0 to see the contents | |
224 | ||
225 | : rmd-init ( rbuf-io-adr rmd-cpu-adr -- ) | |
226 | dup ( rbuf-io-adr rmd-cpu-adr rmd-cpu-adr ) | |
227 | rot ( rmd-cpu-adr rmd-cpu-adr rbuf-io-adr ) | |
228 | d# 20 lshift d# 32 xrshift \ Clear bits[63:44] & bits[11:0] of rbuf-io-adr | |
229 | swap ( rmd-cpu-adr rbuf-io-adr' rmd-cpu-adr ) | |
230 | descriptor32! ( rmd-cpu-adr ) \ Store rbuf-io-adr' in cpu-rmaddr | |
231 | \ rmd is 32 bits! | |
232 | /rmd synciopb \ dma sync using a cpu address | |
233 | ; | |
234 | ||
235 | \ RX completion descriptor initialization | |
236 | \ Fill deadbeef to rcd entry. HW will fill in meaningful values after | |
237 | \ receiving packets. | |
238 | \ | |
239 | : rcd-init ( rcd-cpu-adr -- ) | |
240 | h# dead.beef over ( rcd-cpu-adr deadbeef rcd-cpu-adr ) | |
241 | descriptor64! ( rcd-cpu-adr ) | |
242 | /rcd synciopb \ sunc so that HW will get the newest data | |
243 | ; | |
244 | ||
245 | \ TX descriptor initialization called by transmit. | |
246 | \ o "Transfer length" (TR_LEN, bits[56:44]) of Neptune Tx Packet | |
247 | \ descriptor is 13 bit long , so the TR_LEN mask is 0x1FFF. | |
248 | \ o Mark bit is never set, so we do not request interrust after Tx. | |
249 | \ o Here we store header with tmdheader! and store pointer to data | |
250 | \ buffer with txbufptr! We could have combined them into one. Actually | |
251 | \ we do not need to make Tx descriptor point to the shared Tx data | |
252 | \ buffer before each time we use it because doing it once at | |
253 | \ initialization is enough. | |
254 | \ o Although the MARK bit of the descriptor will cause an interrupt | |
255 | \ after the packet is transmitted only if interrupt is enabled, | |
256 | \ we still set it hoping that it will still cause the MK bit of | |
257 | \ the TX_CS register to be turned on. In send-wait we will | |
258 | \ check that bit to make sure that the packet has indeed been | |
259 | \ transmitted. | |
260 | \ | |
261 | : tmd-init ( txbuf len tmdaddr -- ) | |
262 | swap ( txbuf tmdaddr len ) | |
263 | ||
264 | \ First Check len | |
265 | dup tr-len-mask d# 44 xrshift ( txbuf tmdaddr len len 0x1fff) | |
266 | > if cmn-error[ " Application data too long" ]cmn-end | |
267 | true to restart? \ Set flag, will restart at the end of transmit | |
268 | then | |
269 | d# 44 lshift \ Shift TR_LEN(len) of appl data to bits[56:44] | |
270 | tr-len-mask and ( txbuf tmdaddr len ) \ bits[56:44] is len | |
271 | sof or ( txbuf tmdaddr len|SOF ) \ Every packet is sof | |
272 | mark or ( txbuf tmdaddr len|SOF|MARK ) | |
273 | num-ptr=1 or ( txbuf tmdaddr len|SOF|MARK|NUM_PTR ) | |
274 | over ( txbuf tmdaddr value tmdaddr ) | |
275 | tmdheader! ( txbuf tmdaddr ) | |
276 | swap ( tmdaddr txbuf ) | |
277 | cpu>io-adr ( tmdaddr txbuf-io-adr ) | |
278 | over ( tmdaddr txbuf-io-adr tmdaddr ) | |
279 | txbufptr! \ store the txbuf-io-addr into the pointer part of tmd | |
280 | /tmd synciopb \ dma sync the tmd entry | |
281 | ; | |
282 | ||
283 | ||
284 | \ 14 bits L2_LEN is bit[53:40] of Rx completion ring entry. | |
285 | \ Do shifts so that the 64 bit values contains only the packet length | |
286 | \ | |
287 | : rcdaddr>pkt-len ( rcdaddr -- pkt-len ) | |
288 | descriptor64@ d# 10 lshift d# 50 xrshift | |
289 | ; | |
290 | ||
291 | \ With the cpu address of a Rx Completion Ring descriptor, read the | |
292 | \ contents of the descriptor with descriptor64@, then shift out | |
293 | \ PKTBUFSZ (bits[39:38] of the descriptor entry), which indicates the | |
294 | \ partition of the 8K block buffer. 00=BUFSZ0, b01=BUFSZ1, b10=BUFSZ2 | |
295 | \ b11=single block=8K | |
296 | \ | |
297 | : rcdaddr>pbuf-size ( rcdaddr -- pbuf-len ) | |
298 | descriptor64@ d# 24 lshift d# 63 xrshift | |
299 | ; | |
300 | ||
301 | \ With the cpu address of a Rx Completion Ring descriptor, read the | |
302 | \ contents of the descriptor with descriptor64@, then shift out | |
303 | \ PKT_BUF_ADDR (bits[37:0] of the descriptor entry) | |
304 | \ | |
305 | : rcdaddr>pbuf-addr ( rcdaddr -- pbuf-addr ) | |
306 | descriptor64@ d# 26 lshift d# 26 xrshift | |
307 | ; | |
308 | ||
309 | ||
310 | \ It is easy to figure out rcd# from the (cpu) address of rcd. | |
311 | \ Inside rcd we can find the io-adr of the rx block buffer and | |
312 | \ the offset of the packet inside that block buffer. With | |
313 | \ these two pieces of info, we can find the io-addr of the packet. | |
314 | \ Finally we conver the io-adr of the packet to its cpu-adr | |
315 | \ 43 6 0 ( real io adr) | |
316 | \ RCR 63 37 0 ( RCR bits ) | |
317 | \ | flags |<----- packet-io-addr----|----->| | |
318 | \ bits[5:0]=0 implied | |
319 | ||
320 | : rcdaddr>pkt-cpu-adr ( rcd-cpu-addr -- pkt-cpu-adr ) | |
321 | descriptor64@ d# 26 lshift d# 20 xrshift \ Get rid of flags | |
322 | io>cpu-adr ( pkt-adr ) | |
323 | ; | |
324 | ||
325 | : rcd#>skip ( n -- #skip ) | |
326 | rcd#>rcdaddr descriptor64@ d# 55 xrshift 3 and | |
327 | ; | |
328 | ||
329 | \ Sync buffer contents | |
330 | : sync-buf ( buf-cpu-adr len -- ) | |
331 | over ( buf-cpu-adr len buf-cpu-adr ) | |
332 | cpu>io-adr ( buf-cpu-adr len buf-io-adr ) | |
333 | swap ( buf-cpu-adr buf-io-adr len ) | |
334 | dma-sync ( ) | |
335 | ; | |
336 | ||
337 | variable nextrcd | |
338 | variable nexttmd | |
339 | ||
340 | \ Get current rx completion descriptor ring pointer in CPU address space. | |
341 | : nextrcd@ ( -- rcd-cpu-addr ) nextrcd x@ ; | |
342 | ||
343 | \ Get current tx message descriptor ring pointer in CPU address space. | |
344 | : nexttmd@ ( -- tmd-cpu-addr ) nexttmd x@ ; | |
345 | ||
346 | \ Save current rx completion/tx message descriptor ring pointer in | |
347 | \ CPU address space. | |
348 | \ Note that we write a rcd address to nextrcd variable, not contents of | |
349 | \ a rcd to nextrcd | |
350 | \ | |
351 | : nextrcd! ( rcdaddr -- ) nextrcd x! ; | |
352 | ||
353 | : nexttmd! ( tmdaddr -- ) nexttmd x! ; | |
354 | ||
355 | : tx-config ( chan -- ) | |
356 | 0 tx-addr-md! \ Use 64 bit addressing mode | |
357 | ||
358 | niu? 0= if | |
359 | \ Define only page0, page1 will not be checked. | |
360 | \ Set the FUNC field (bits[3:2]) of register TX_LOG_PAGE_VLD to | |
361 | \ port (it equals PCI function number) which will "be used when | |
362 | \ sending request across PCI bus". | |
363 | port 2 lshift page0 or ( chan# val ) \ val = FUNC|PAGE0 | |
364 | over tx-log-page-vld-i! \ Use only chan#-th Rx DMA channel | |
365 | ||
366 | \ All mask bits are set to 0 so there is no relocation. | |
367 | 0 over tx-log-mask1-i! | |
368 | 0 over tx-log-value1-i! | |
369 | 0 over tx-log-mask2-i! | |
370 | 0 over tx-log-value2-i! | |
371 | 0 over tx-log-page-relo1-i! \ Since mask=0, this doesn't matter | |
372 | 0 over tx-log-page-relo2-i! \ Since mask=0, this doesn't matter | |
373 | 0 over tx-log-page-hdl-i! \ Assume no need to extend to 64 bits | |
374 | then | |
375 | drop | |
376 | ||
377 | \ Weight of Deficit Round Robin for multiple Tx DMA chan to share a port. | |
378 | h# 2710 chan# txc-dma-max-i! | |
379 | ||
380 | \ Do not inject parity error to any of the 24 DMA | |
381 | 0 tdmc-inj-par-err! | |
382 | ||
383 | \ Debug select register, set to initial value | |
384 | 0 tdmc-dbg-sel! | |
385 | ||
386 | \ TDMC training vector register, set to default value | |
387 | 0 tdmc-training-vector! | |
388 | ; | |
389 | ||
390 | ||
391 | : rx-config ( chan -- ) | |
392 | \ System clock divider, granularity for dma timeout. | |
393 | h# 1d4c rx-dma-ck-div! | |
394 | ||
395 | set-default-rdc-for-my-port | |
396 | ||
397 | \ Use 64 bit addressing mode. But test shows that bit0 = 1 (MODE32) | |
398 | \ also works. | |
399 | 0 rx-addr-md! | |
400 | ||
401 | \ Weights of Deficit Round Robin (DRR) for 4 ports | |
402 | h# 400 pt-drr-wt0! | |
403 | h# 400 pt-drr-wt1! | |
404 | h# 66 pt-drr-wt2! | |
405 | h# 66 pt-drr-wt3! | |
406 | ||
407 | niu? 0= if | |
408 | \ Logic pages related registers. All mask bits are set to 0 | |
409 | \ so there is no relocation. Only page0 is defined so page1 will | |
410 | \ not be checked. Bits[3:2] of register RX_LOG_PAGE_VLD (FUNC) | |
411 | \ is set to the PCI function number (which is equal to the port | |
412 | \ number). The FUNC field is used when we make PCI request | |
413 | port 2 lshift page0 or ( chan x1 ) | |
414 | over rx-log-page-vld-i! | |
415 | ||
416 | 0 over rx-log-mask1-i! | |
417 | 0 over rx-log-val1-i! | |
418 | 0 over rx-log-mask2-i! | |
419 | 0 over rx-log-val2-i! | |
420 | 0 over rx-log-page-relo1-i! \ Since mask=0, this doesn't matter | |
421 | 0 over rx-log-page-relo2-i! \ Since mask=0, this doesn't matter | |
422 | 0 over rx-log-page-hdl-i! \ Assume no need for 64 bits | |
423 | ||
424 | \ Set MBADDR_L=0, OFFSET=no, FULL_HDR | |
425 | full-hdr over rxdma-cfig2-i! | |
426 | else | |
427 | io-rmbox xlsplit nip ( chan addr.lo ) | |
428 | over rxdma-cfig1-i! ( chan ) | |
429 | ||
430 | \ RXMAC configuration2 | |
431 | io-rmbox xlsplit drop ( chan addr.hi ) | |
432 | full-hdr or ( chan addr.hi' ) | |
433 | over rxdma-cfig2-i! ( chan ) | |
434 | then ( chan ) | |
435 | ||
436 | \ Disable mailbox | |
437 | dup rx-dma-ctl-stat-i@ mex invert and ( chan x1 ) | |
438 | swap rx-dma-ctl-stat-i! ( ) | |
439 | ; | |
440 | ||
441 | : wait-sng-state ( -- flag ) | |
442 | chan# tx-cs-i@ sng-state and 0<> \ SNG_STATE=1 if reset is done | |
443 | ; | |
444 | ||
445 | : wait-for-dma-engine-to-stop ( -- ok? ) | |
446 | d# 3000 ['] wait-sng-state wait-status | |
447 | dup 0= if | |
448 | cmn-error[ " SNG_STATE did not reset" ]cmn-end | |
449 | then | |
450 | ; | |
451 | ||
452 | \ Check if RST is cleared by HW and if QST is 1 which | |
453 | \ incidates all state machines are in initial state | |
454 | : wait-rx-dma-reset ( chan# -- flag ) | |
455 | dup rxdma-cfig1-i@ rxdma-rst and 0= \ RST = 0 ? | |
456 | swap rxdma-cfig1-i@ rxdma-qst and 0<> and \ QST = 1 ? | |
457 | ; | |
458 | ||
459 | \ PRM: "First set EN bit to zero, and then set RST bit to 1. | |
460 | \ After RST bit is cleared by hardware and the QST bit is set | |
461 | \ to 1, software may then start configuring the DMA channel. | |
462 | \ After configuration, software may then set the EN bit to 1 | |
463 | \ to enable the DMA. | |
464 | \ | |
465 | \ First disable Rx DMA by setting EN bit of RXDMA_CFG1 to 0 | |
466 | \ then set RST bit and poll it until hardware clears it. | |
467 | \ After this call, we can start configuring the DMA channel. | |
468 | \ We will enable the Rx DMA in enable-tx-dma-channel which is | |
469 | \ called at the end of (setup-link). | |
470 | \ | |
471 | : reset-rx-dma-channel ( chan# -- ok? ) | |
472 | dup dup | |
473 | rxdma-cfig1-i@ ( chan# chan# val ) | |
474 | rxdma-en invert and swap ( chan# val&[~EN] chan# ) | |
475 | rxdma-cfig1-i! ( chan# ) | |
476 | dup ( chan# chan# ) | |
477 | \ Just set RST bit, do not read first | |
478 | rxdma-rst swap ( chan# val|RST chan# ) | |
479 | rxdma-cfig1-i! ( chan# ) | |
480 | d# 100 swap ( 100 chan# ) | |
481 | ['] wait-rx-dma-reset wait-status-with-arg | |
482 | dup 0= if ( ok? ) | |
483 | cmn-error[ " reset-rx-dmc-channel failed" ]cmn-end | |
484 | then | |
485 | ; | |
486 | ||
487 | \ The PRM states: | |
488 | \ "The DMA channel may be reset by writing a 1 to the RST bit. When | |
489 | \ reset is completed, hardware will set the RST_STATE bit to 1." | |
490 | \ In the description of the RST bit of the TX_CS register, PRM also | |
491 | \ mentions "Hardware will clear this bit after reset is completed." | |
492 | \ We check the completion of DMA reset by checking both RST=0 and | |
493 | \ RST_STATE=1. | |
494 | \ | |
495 | : wait-tx-dma-reset ( chan# -- flag ) | |
496 | dup tx-cs-i@ rst and 0= | |
497 | swap tx-cs-i@ rst-state and 0<> and | |
498 | ; | |
499 | ||
500 | : cleanup-tx-dma-channel ( chan# -- ) | |
501 | \ Must clear TAIL because the HW will not clear it. If driver | |
502 | \ does not clear TAIL, HEAD will follow TAIL to the non-zero value | |
503 | \ when enable-tx-dma-channel is called and that will cause trouble. | |
504 | 0 swap ( 0 chan ) | |
505 | tx-ring-kick-i! ( ) | |
506 | ||
507 | \ Must clear WRAP so that both sw and hw start with WRAP=0 | |
508 | wrap=0 to wrap | |
509 | ; | |
510 | ||
511 | \ First sets the RST bit of TX_CS to 1 and poll RST and RST_STATE | |
512 | \ until HW clears RST and sets RST_STATE. RST_STATE=1 indicates that | |
513 | \ the DMA channel is in a stall state and is waiting for the driver | |
514 | \ to do configuration. We will bring the DMA channel out of stall | |
515 | \ state later by clearing RST_STATE in word "enable-tdma" after DMA | |
516 | \ configuration is done. | |
517 | \ | |
518 | : reset-tx-dma-channel ( chan -- ok? ) | |
519 | dup ( chan chan ) | |
520 | tx-cs-i@ ( chan val ) | |
521 | rst-state and if ( chan ) \ Already in reset state | |
522 | true ( chan true ) | |
523 | else ( chan ) | |
524 | stop-n-go over ( chan STOP_N_GO chan ) | |
525 | tx-cs-i! ( chan ) | |
526 | wait-for-dma-engine-to-stop ( chan ok? ) | |
527 | 0= if ( chan ) | |
528 | cmn-error[ " TX DMA engine did not stop" ]cmn-end | |
529 | false ( chan false ) | |
530 | else | |
531 | dup tx-cs-i@ ( chan val ) | |
532 | rst-state ( chan val RST_STATE ) | |
533 | and 0= if ( chan ) \ Reset if not in reset state | |
534 | RST over ( chan RST chan ) | |
535 | tx-cs-i! ( chan ) | |
536 | then ( chan ) | |
537 | dup wait-tx-dma-reset ( chan ok? ) | |
538 | dup 0= if cmn-error[ " TX DMA engine did not reset" ]cmn-end then | |
539 | then ( chan ok? ) | |
540 | then ( ok? ) | |
541 | swap cleanup-tx-dma-channel ( ok? ) | |
542 | ; | |
543 | ||
544 | : reset-dma-channel ( chan -- ok? ) | |
545 | niu? if | |
546 | cleanup-tx-dma-channel true ( ok? ) | |
547 | else ( chan ) | |
548 | dup reset-tx-dma-channel ( chan ok1? ) | |
549 | swap reset-rx-dma-channel ( ok1? ok2? ) | |
550 | and | |
551 | then | |
552 | ; | |
553 | ||
554 | \ Initialize RX descriptor and completion rings for one DMA channel | |
555 | \ | |
556 | : init-rxrings ( -- ) | |
557 | #rmds 4 - 0 do | |
558 | \ Prepare args for rmd-init ( rbug-io-adr rmd-cpu-adr -- ) | |
559 | i rbuf#>rbuf-io-adr \ dev addr of ith 8K data block | |
560 | i rmd#>rmdaddr \ cpu addr of ith Rx Descriptor | |
561 | rmd-init | |
562 | loop | |
563 | ||
564 | #rcds 0 do | |
565 | i rcd#>rcdaddr \ Prepare arg for rcd-init | |
566 | rcd-init \ Fill 0 to each of 256 rcd entry | |
567 | loop | |
568 | ||
569 | rcd0 nextrcd! | |
570 | ; | |
571 | ||
572 | \ Initialize a Tx descriptor ring for one DMA channel. | |
573 | \ Use only one Tx buffer. | |
574 | \ The address stored in the Tx packet descriptors is io address. | |
575 | \ | |
576 | : init-txring ( -- ) | |
577 | #tmds 0 do | |
578 | io-tbuf0 \ Addr of the 4K Tx data buffer | |
579 | i tmd#>tmdaddr \ Starting addr of i-th descriptor | |
580 | txbufptr! \ Store io-tbuf0 in the 2nd 8bytes of tmd | |
581 | loop \ All 64 tmds point to the same 4K data buffer | |
582 | tmd0 nexttmd! | |
583 | ; | |
584 | ||
585 | ||
586 | \ These "cached" Rx errors are updated in update-rx-err-status. | |
587 | \ This word set fatal rx error flag if any of the cached error | |
588 | \ is non-zero. | |
589 | \ | |
590 | : fatal-rx-errors? ( -- flag ) | |
591 | rx-dma-err-status | |
592 | rdmc-pre-err-status or | |
593 | rdmc-sha-err-status or | |
594 | rx-ctl-dat-fifo-status or | |
595 | ipp-int-err-status or | |
596 | ; | |
597 | ||
598 | : fatal-tx-errors? ( -- glag ) | |
599 | tx-dma-err-status | |
600 | ; | |
601 | ||
602 | 0 value txerr-status \ TX MAC Error status bits | |
603 | 0 value rxerr-status \ RX MAC Error status bits | |
604 | ||
605 | defer restart-net ( -- ok? ) | |
606 | ['] true to restart-net | |
607 | ||
608 | : update-tx-err-status ( -- ) | |
609 | \ Fatal Tx DMA errors | |
610 | chan# tx-cs-i@ tx-dma-fatal-errs and | |
611 | tx-dma-err-status or to tx-dma-err-status | |
612 | ||
613 | \ MAC Tx errors | |
614 | update-mac-tx-err-stat | |
615 | ||
616 | \ XPCS, PCS also have error registers, but they | |
617 | \ are not listed as fatal by the PRM. | |
618 | ; | |
619 | ||
620 | : update-rx-err-status ( chan -- ) | |
621 | \ Fatal Rx DMA errors | |
622 | rx-dma-ctl-stat-i@ rx-dma-fatal-errs-mask and ( stat ) | |
623 | rx-dma-err-status or dup 0<> if ( stat ) | |
624 | cmn-error[ dup " rx-dma-fatal-errs = %x" ]cmn-end then | |
625 | to rx-dma-err-status ( ) | |
626 | ||
627 | rdmc-pre-par-err@ rdmc-pre-par-errs-mask and ( stat ) | |
628 | rdmc-pre-err-status or dup 0<> if ( stat ) | |
629 | cmn-error[ dup " rdmc-pre-par-errs = %x" ]cmn-end then | |
630 | to rdmc-pre-err-status ( ) | |
631 | ||
632 | rdmc-sha-par-err@ rdmc-sha-par-errs-mask and ( stat ) | |
633 | rdmc-sha-err-status or dup 0<> if ( stat ) | |
634 | cmn-error[ " rdmc-sha-par-errs = %x" ]cmn-end then | |
635 | to rdmc-sha-err-status ( ) | |
636 | ||
637 | \ bits[3:0] of RX_CTL_DAT_FIFO_STAT are IPP_EOP_ERR for port3:0 | |
638 | \ bits[7:4] of RX_CTL_DAT_FIFO_STAT are ZCP_EOP_ERR for port3:0 | |
639 | \ Since we do not initialize other ports, we should care about | |
640 | \ our own port only. For example, if we are using port 1, we | |
641 | \ should only care about bit1 and bit 4. | |
642 | \ Bit 8 (h# 80) is for ID mismatch. Bits[63:9] are RSVD | |
643 | h# 11 port lshift h# 80 or ( mask ) | |
644 | rx-ctl-dat-fifo-stat@ and ( stat ) | |
645 | rx-ctl-dat-fifo-errs-mask and ( stat' ) | |
646 | rx-ctl-dat-fifo-status or ( stat'' ) | |
647 | dup 0<> if ( stat'' ) | |
648 | cmn-error[ " rx-ctl-dat-fifo-errs = %x" ]cmn-end | |
649 | then ( stat'' ) | |
650 | to rx-ctl-dat-fifo-status ( ) | |
651 | ||
652 | \ Fatal IPP errors | |
653 | port ipp-int-stat-p@ ( stat ) | |
654 | ipp-int-stat-errs-mask and ( stat' ) | |
655 | ipp-int-err-status or dup 0<> if ( stat'' ) | |
656 | cmn-error[ " ipp-int-stat-errs = %x" ]cmn-end then | |
657 | to ipp-int-err-status ( ) | |
658 | ||
659 | \ Serious XMAC or BMAC errors, but we do not set the restart? flag. | |
660 | update-mac-rx-err-stat ( ) | |
661 | ||
662 | \ XPCS, PCS also have error registers, but | |
663 | \ none of them are fatal so we do not count them | |
664 | ; | |
665 | ||
666 | \ Display transmit errors. | |
667 | : .transmit-errors ( -- ) | |
668 | \ Fatal Tx DMA errors | |
669 | tx-dma-err-status tx-ring-oflow and 0<> if ." TX_RING_OFLOW" cr then | |
670 | tx-dma-err-status pref-buf-par-err and 0<> if ." PREF_BUF_PAR_ERR" cr then | |
671 | tx-dma-err-status nack-pref and 0<> if ." NACK_PREF" cr then | |
672 | tx-dma-err-status nack-pkt-rd and 0<> if ." NACK_PKT_RD" cr then | |
673 | tx-dma-err-status conf-part-err and 0<> if ." CONF_PART_ERR" cr then | |
674 | tx-dma-err-status pkt-prt-err and 0<> if ." PKT_PRT_ERR" cr then | |
675 | .mac-tx-err | |
676 | ; | |
677 | ||
678 | \ Display receive errors. | |
679 | : .receive-errors ( -- ) | |
680 | ." Rx Error: " cr | |
681 | \ Fatal DMA Error | |
682 | rx-dma-err-status rbr-tmout and 0<> if ." BR_TMOUT" cr then | |
683 | rx-dma-err-status rsp-cnt-err and 0<> if ." RSP_CNT_ERR" cr then | |
684 | rx-dma-err-status byte-en-bus and 0<> if ." BYTE_EN_BUS" cr then | |
685 | rx-dma-err-status rsp-dat-err and 0<> if ." RSP_DAT_ERR" cr then | |
686 | rx-dma-err-status rcr-ack-err and 0<> if ." RCR_ACK_ERR" cr then | |
687 | rx-dma-err-status dc-fifo-err and 0<> if ." DC_FIFO_ERR" cr then | |
688 | rx-dma-err-status rcr-sha-par and 0<> if ." RCR_SHA_PAR" cr then | |
689 | rx-dma-err-status rbr-pre-par and 0<> if ." RBR_PRE_PAR" cr then | |
690 | rx-dma-err-status config-err and 0<> if ." CONFIG_ERR" cr then | |
691 | rx-dma-err-status rcrincon and 0<> if ." CRINCON" cr then | |
692 | rx-dma-err-status rcrfull and 0<> if ." RCRFULL" cr then | |
693 | rx-dma-err-status rbrfull and 0<> if ." RBRFULL" cr then | |
694 | rx-dma-err-status rbrlogpage and 0<> if ." RBRLOGPAGE" cr then | |
695 | rx-dma-err-status cfiglogpage and 0<> if ." CFIGLOGPAGE" cr then | |
696 | rdmc-pre-err-status pre-par-err and 0<> if ." RDMC_PRE_PAR.ERR" cr then | |
697 | rdmc-pre-err-status pre-par-merr and 0<> if ." RDMC_PRE_PAR.MERR" cr then | |
698 | rdmc-sha-err-status sha-par-err and 0<> if ." RDMC_SHA_PAR.ERR" cr then | |
699 | rdmc-sha-err-status sha-par-merr and 0<> if ." RDMC_SHA_PAR.MERR" cr then | |
700 | rx-ctl-dat-fifo-status id-mismatch and 0<> if ." ID_MISMATCH" cr then | |
701 | rx-ctl-dat-fifo-status zcp-eop-err and 0<> if ." ZCP_EOP_ERR" cr then | |
702 | rx-ctl-dat-fifo-status ipp-eop-err and 0<> if ." IPP_EOP_ERR" cr then | |
703 | \ Fatal IPP errors | |
704 | ipp-int-err-status sop-miss and 0<> if ." SOP_MISS" cr then | |
705 | ipp-int-err-status eop-miss and 0<> if ." EOP_MISS" cr then | |
706 | ipp-int-err-status pfifo-und and 0<> if ." PFIFO_UND" cr then | |
707 | \ Serious XMAC or BMAC errors | |
708 | .mac-rx-err | |
709 | ; | |
710 | ||
711 | : transmit-errors? ( -- flag ) | |
712 | update-tx-err-status | |
713 | ||
714 | \ Set restart? flag if update-tx-err-status has detected fatal error | |
715 | fatal-tx-errors? dup if | |
716 | true to restart? | |
717 | then | |
718 | ||
719 | \ Following error does not trigger restart. Simply report to caller | |
720 | mactype xmac = if | |
721 | tx-xmac-err-status or | |
722 | else | |
723 | tx-bmac-err-status or | |
724 | then | |
725 | ; | |
726 | ||
727 | : receive-errors? ( chan -- err|0 ) | |
728 | update-rx-err-status ( -- ) | |
729 | ||
730 | \ fatal-rx-errors? returns true if update-rx-err-status has recorded | |
731 | \ a fatal error | |
732 | fatal-rx-errors? if | |
733 | true to restart? | |
734 | then | |
735 | ||
736 | \ The following errors are also cached by update-rx-err-status | |
737 | \ but we simply report them to the caller instead of treating them | |
738 | \ as fatal. | |
739 | mactype xmac = if | |
740 | rx-xmac-err-status ( err ) \ 0 if no error | |
741 | else | |
742 | rx-bmac-err-status | |
743 | then | |
744 | ; | |
745 | ||
746 | \ Clear TX error bits in cached values. | |
747 | : clear-tx-errors ( -- ) | |
748 | tx-dma-err-status tx-dma-fatal-errs invert and | |
749 | to tx-dma-err-status | |
750 | clear-mac-tx-err | |
751 | ; | |
752 | ||
753 | ||
754 | \ Clear RX error bits in cached values. | |
755 | : clear-rx-errors ( -- ) | |
756 | rx-dma-err-status rx-dma-fatal-errs-mask invert and to | |
757 | rx-dma-err-status | |
758 | rdmc-pre-err-status rdmc-pre-par-errs-mask invert and to | |
759 | rdmc-pre-err-status | |
760 | rdmc-sha-err-status rdmc-sha-par-errs-mask invert and to | |
761 | rdmc-sha-err-status | |
762 | rx-ctl-dat-fifo-status rx-ctl-dat-fifo-errs-mask invert and to | |
763 | rx-ctl-dat-fifo-status | |
764 | ipp-int-err-status ipp-int-stat-errs-mask invert and to | |
765 | ipp-int-err-status | |
766 | clear-mac-rx-err | |
767 | ; | |
768 | ||
769 | \ Clear the rst-state bit to start | |
770 | \ the dma after we have done configuration. | |
771 | \ | |
772 | : enable-tx-dma-channel ( chan -- ) | |
773 | dup tx-cs-i@ rst-state invert and ( chan mod_val ) | |
774 | swap tx-cs-i! | |
775 | ; | |
776 | ||
777 | : enable-rx-dma-channel ( chan -- ) | |
778 | dup rxdma-cfig1-i@ rxdma-en or swap rxdma-cfig1-i! | |
779 | ; | |
780 | ||
781 | ||
782 | \ We have a tx descriptor ring constructed already, now tell the | |
783 | \ Neptune about it. | |
784 | \ | |
785 | : init-tx-regs ( chan -- ) | |
786 | \ Set length of ring to 64 and set STADDE_BASE:STADDR to the | |
787 | \ the IO address of the Tx ring | |
788 | len=64 io-tmd0 or ( chan x1 ) | |
789 | over tx-rng-cfig-i! ( chan ) | |
790 | ||
791 | \ Disable events that may trigger interrrups. | |
792 | tx-dma-ent-dis-msk swap tx-ent-msk-i! | |
793 | ; | |
794 | ||
795 | \ last-partition-of-rbuf? depends on how we partion the 8K | |
796 | \ Rx block buffer. We divide it into 4K and 2K | |
797 | \ | |
798 | : init-rx-regs ( chan -- ) | |
799 | \ Initialize rmd related config registers. Tell Neptune that | |
800 | \ rmd has #rmds entries. Its base addr is io-rmd0 | |
801 | #rmds d# 48 lshift io-rmd0 or ( chan x1 ) | |
802 | over rbr-cfig-a-i! ( chan ) | |
803 | ||
804 | \ Tell Neptune that each block is 8K, and it should partition | |
805 | \ the blocks into 4K, 4K and 2K sub-blocks. 2K is always greater | |
806 | \ than the size of non-jumbo Ethernet frames. | |
807 | bksize=8k ( chan x2 ) | |
808 | vld2 or bufsz2=4k or ( chan x2' ) | |
809 | vld1 or bufsz1=2k or ( chan x2'' ) | |
810 | vld0 or bufsz0=2k or ( chan x2''' ) | |
811 | over rbr-cfig-b-i! ( chan ) | |
812 | ||
813 | \ Initialize rcd related config registers | |
814 | \ Tell Neptune where the RCR is and the number of RCR entries | |
815 | \ in the ring. | |
816 | ||
817 | #rcds d# 48 lshift io-rcd0 or ( chan x3 ) | |
818 | over rcrcfig-a-i! ( chan ) | |
819 | ||
820 | pthres=1 entout or timeout=1 or ( chan x4 ) | |
821 | over rcrcfig-b-i! ( chan ) | |
822 | ||
823 | \ Disable events that may trigger interrups. | |
824 | rx-dma-ent-disable-mask over rx-dma-ent-msk-i! | |
825 | ||
826 | \ Enable Weighted Random Early Discard (WRED) to prevent the rings | |
827 | \ from overflow when there is an attack targeting at the Neptune's | |
828 | \ MAC address. | |
829 | #rcds 2 / shift-thre-syn lshift ( chan x5 ) | |
830 | 0 shift-win-syn lshift or ( chan x5' ) | |
831 | #rcds 2 / shift-thre lshift or ( chan x5'' ) | |
832 | 0 shift-win lshift or ( chan x5''' ) | |
833 | chan# rdc-red-para-i! ( chan ) | |
834 | ||
835 | \ Set OPMODE bit to enable WRED for all DMA. But we have set | |
836 | \ parameters for channel 0 only. other channel will not be able | |
837 | \ to receive because the default value of RDC_RED_PARA causes | |
838 | \ the WRED to drop all packets. Init value 0x3456 is an arbitrary | |
839 | \ number that is known to have worked. | |
840 | h# 3456 opmode=1 or red-ran-init! ( chan ) | |
841 | ||
842 | #rmds 4 - dup to last-rmd-idx ( chan x6 ) | |
843 | over rbr-kick-i! \ Kick some to begin | |
844 | ||
845 | \ As advised by PRM, write to RX_DMA_CTL_STAT to clear bit 35 | |
846 | \ so that RBR_EMPTY is cleared. | |
847 | over rx-dma-ctl-stat-i@ rbr-empty or over rx-dma-ctl-stat-i! | |
848 | ||
849 | \ Disable mailbox update. | |
850 | dup rx-dma-ctl-stat-i@ MEX invert and swap rx-dma-ctl-stat-i! | |
851 | ; | |
852 | ||
853 | \ Neptune has 69 (LD) interrupt sources and 64 (LDG) logic groups for | |
854 | \ generating interrupts. set-ldg has set LDGs as follows, | |
855 | \ LDG0 for Rx DMA0 (LD0), Tx DMA0 (LD32) and port-th-MAC (LD64,65,66,67). | |
856 | \ LDG 63 LD68 Device Error. After calling this word, events from these | |
857 | \ two logic groups will not be able to trigger interrupt | |
858 | \ | |
859 | : disable-all-intrs ( -- ) | |
860 | ldg0 ldgimgn-i@ arm invert and ldg0 ldgimgn-i! | |
861 | ldg63 ldgimgn-i@ arm invert and ldg63 ldgimgn-i! | |
862 | ||
863 | disable-mac-intr | |
864 | ; | |
865 | ||
866 | : init-dma-channel ( chan -- ) | |
867 | dup tx-config ( chan ) | |
868 | dup rx-config ( chan ) | |
869 | init-txring ( chan ) | |
870 | init-rxrings ( chan ) | |
871 | dup init-tx-regs ( chan ) | |
872 | dup init-rx-regs ( chan ) | |
873 | dup enable-tx-dma-channel ( chan ) | |
874 | enable-rx-dma-channel ( ) | |
875 | disable-all-intrs ( ) | |
876 | ; | |
877 | ||
878 | ||
879 | \ Tell the HW that we have received a packet so that the HW will | |
880 | \ update its ring head. Update nextrcd which is a software copy | |
881 | \ of the addr of next rcd (Rx Completion-ring Descriptor) | |
882 | \ | |
883 | : sync-and-update-nextrcd ( -- ) | |
884 | \ Free current completion descriptor | |
885 | nextrcd@ rcd-init ( ) | |
886 | ||
887 | \ Update completion ring info, clear low 32 bits | |
888 | chan# rx-dma-ctl-stat-i@ ( x1 ) | |
889 | d# 31 lshift d# 31 xrshift ( x1' ) | |
890 | ptrread=1 or pktread=1 or ( x1'' ) | |
891 | chan# rx-dma-ctl-stat-i! ( ) | |
892 | ||
893 | \ Update pointer to next completion descriptor | |
894 | nextrcd@ rcdaddr>rcd# 1 + ( x2 ) | |
895 | rcd#>rcdaddr nextrcd! ( ) | |
896 | ; | |
897 | ||
898 | ||
899 | \ If an 8K block buffer has been used up, then we reclaim | |
900 | \ it kick it back to the Neptune. We can get the starting | |
901 | \ address of the 8K block buffer a pakcet belongs to by clearing | |
902 | \ the lower 13 bits of the the packet address (rcdaddr). Note that | |
903 | \ since the rmd omits the lower 12 bit for 4K boundary, bit 12 | |
904 | \ is the LSB in rmd, which MUST be 0 in order to use 8K block buffers. | |
905 | \ Original content of rcd: | |
906 | \ 63 RCD 37 6 0 | |
907 | \ +--------------------+-----------------+-------+ | |
908 | \ | flags | bits[43:6] of pkt adr | | |
909 | \ +--------------------+-----------------+-------+ | |
910 | \ actual addr: 43 12 6 (bits[5:0]=0 for | |
911 | \ 64B alignment) | |
912 | \ Above io address of the packet reported by the hardware via | |
913 | \ RCR may not be the same as the starting io address of the | |
914 | \ 8K block buffer (because the packet may not be the first | |
915 | \ partition in that block buffer). So we need to clear bits[12:6] | |
916 | \ in addition to the implied zero bits[5:0] to make a 8K aligned | |
917 | \ address. That is the starting address of the 8K block buffer | |
918 | \ to be reclaimed and kicked back to the hardware. | |
919 | \ We xrshift 7 bits then lshift 13 bits to clear the lowest 13 bits. | |
920 | ||
921 | \ 63 43 0 | |
922 | \ +-------------+--------------------------------+ | |
923 | \ | | addr bits[43:13] 0000000000000| | |
924 | \ +-------------+--------------------------------+ | |
925 | \ | |
926 | : reclaim-rx-buffer ( nextrcdaddr -- ) | |
927 | descriptor64@ \ Got contents of a Rx completion ring entry | |
928 | ||
929 | \ Shift to get the starting address of the 8K block buffer | |
930 | \ which the packet is in. | |
931 | 7 xrshift d# 13 lshift ( rbuf-io-adr ) | |
932 | last-rmd-idx ( rbuf-io-adr rmd# ) \ Get recorded kicked rmd | |
933 | \ index (Its initial value | |
934 | \ is #rmds - 4) | |
935 | rmd#>rmdaddr ( rbuf-io-adr rmd-cpu-adr ) | |
936 | rmd-init ( ) \ Puts rbuf-io-adr in a rmd in cpu addr | |
937 | ||
938 | \ Update soft kick state and kick register | |
939 | last-rmd-idx 1+ \ New last-kicked-rmd-idx | |
940 | ||
941 | 1 chan# rbr-kick-i! \ Different from Cassini which kicks | |
942 | \ the rmd index, here we kick BKADD, which | |
943 | \ stands for "number of BlocK buffer ADDed. | |
944 | ||
945 | \ If rmd index is greater than #rmds, wrap it back to 0 | |
946 | #rmds mod | |
947 | to last-rmd-idx \ Even if we did not kick a rmd, still record it | |
948 | ; | |
949 | ||
950 | ||
951 | \ last-partition-of-rbuf? checks if the packet just received | |
952 | \ took the last partition of the 8K block buffer. If yes, | |
953 | \ then we recycle the whole 8K block buffer; if not, then we | |
954 | \ wait until all the partitions in the 8K block buffer | |
955 | \ have been used by the hardware. | |
956 | \ | |
957 | \ Neptune's Rx completion ring entry carries the addr of | |
958 | \ a packet buffer inside a 8K block buffer. We can figure out | |
959 | \ if a packet is the last packet in the block buffer by checking | |
960 | \ its address. For 8K block buffers, the address of the last | |
961 | \ packet has the following bits as 1s | |
962 | \ 8K block buffer | |
963 | \ If pkt uses size 0(1K) and if bits[12:10] ==111 --> last packet | |
964 | \ If pkt uses size 1(2K) and if bits[12:11] ==11 --> last packet | |
965 | \ If pkt uses size 2(4K) and if bits[12] ==1 --> last packet | |
966 | \ | |
967 | \ For example, below is a Rx completion ring entry for a packet | |
968 | \ that uses a 1K partition inside the 8K buffer block. If bits[12:10] | |
969 | \ of a size0 partition are 111 (which are bits[8:6] of the RCR entry), | |
970 | \ then this packet has taken the last 1K sub-block of the 8K buffer. | |
971 | \ (Bits[5:0] of the packet address are outside of the RCR entry | |
972 | \ because the the address is 64B aligned) | |
973 | \ | |
974 | \ 63 37 6 0 | |
975 | \ +--------------------+-----------------+-------+ | |
976 | \ | flags | high addr |111XXXX|xxxxxx | |
977 | \ +--------------------+-----------------+-------+ | |
978 | \ 43 12 9 6 543210 | |
979 | \ | |
980 | : last-partition-of-rbuf? ( nextrcdaddr -- need-to-release? ) | |
981 | dup rcdaddr>pbuf-addr swap ( pktbuf-addr nextrcdaddr ) | |
982 | rcdaddr>pbuf-size \ return 0,1,2 represent size0,1,2 | |
983 | ||
984 | \ Switch based on the PKTBUFSZ field of the Receive | |
985 | \ Completion Ring Entry | |
986 | case | |
987 | \ If bits[39:38] (PKTBUFSZ) are zero, it means this packet | |
988 | \ was put in a BUFSZ0(set to 2K) partition. The partition | |
989 | \ is the last one in the 8K block buffer if bits[12:10] of | |
990 | \ the RCR descriptor are 11. Therefore we must release the | |
991 | \ whole 8K buffer. | |
992 | bufsz0 of | |
993 | d# 57 lshift d# 62 xrshift b# 11 = \ bufsz0=2k | |
994 | endof | |
995 | ||
996 | \ If bits[39:38] (PKTBUFSZ) are b01, this packet was | |
997 | \ put in a BUFSZ1(was also set to 2K) partition. The partition | |
998 | \ is the last one in the 8K block buffer if bits[12:11] | |
999 | \ of the RCD are 11. | |
1000 | bufsz1 of | |
1001 | d# 57 lshift d# 62 xrshift b# 11 = | |
1002 | endof | |
1003 | ||
1004 | \ If bits[39:38] (PKTBUFSZ) are b10, this packet was | |
1005 | \ put in a BUFSZ2(set to 4K) partition. The partition | |
1006 | \ is the last one in the 8K block buffer if bit12 of the | |
1007 | \ RCD is 1. | |
1008 | bufsz2 of | |
1009 | d# 57 lshift d# 63 xrshift b# 1 = | |
1010 | endof | |
1011 | ||
1012 | singleblk of | |
1013 | \ If packet takes whole 8K block, then set need-to-release? = true | |
1014 | drop true | |
1015 | endof | |
1016 | ||
1017 | \ drop the contents of descriptor | |
1018 | dup cmn-error[ " Bad packet size: 0x%x " ]cmn-end | |
1019 | endcase | |
1020 | ; | |
1021 | ||
1022 | : return-rx-buffer ( rcd-cpu-addr -- ) | |
1023 | dup last-partition-of-rbuf? ( rcd-cpu-addr last-partion-in-8K-block? ) | |
1024 | if ( rcd-cpu-addr ) | |
1025 | reclaim-rx-buffer ( ) \ Reclaim one 8K data block | |
1026 | else | |
1027 | drop | |
1028 | then | |
1029 | sync-and-update-nextrcd \ Clear curr rcd, syncronize with HW by | |
1030 | \ telling HW the pkt and the rcd we just read | |
1031 | \ and move to next rcd regardless reclaim or not | |
1032 | ; | |
1033 | ||
1034 | ||
1035 | \ Do nothing if receive-errors? reports false | |
1036 | \ | |
1037 | : process-receive-errors ( chan -- ) | |
1038 | receive-errors? if \ chan is consumed by receive-errors? | |
1039 | .receive-errors | |
1040 | restart? if \ restart? is set in receive-errors? if we | |
1041 | restart-net drop \ see any fatal or serious error. Note that | |
1042 | then \ MAC Rx overflow error does not set restart? | |
1043 | clear-rx-errors \ flag, we only print and clear it here. | |
1044 | then | |
1045 | ; | |
1046 | ||
1047 | \ Before driver reads data received by the HW, it first polls to see | |
1048 | \ if there is any packet ready to be read by SW by checking the contents | |
1049 | \ of the next RCR, if its packet address is not the initialized value 0, | |
1050 | \ then HW has something for us. To check the contents of the next | |
1051 | \ RCR entry, we first call synciopb to make sure that device will do | |
1052 | \ a DMA sync to putback the latest data into the dma memory before we | |
1053 | \ read it. After synciopb, we can confidently read the contents of | |
1054 | \ the next RCR entry to check if there is any packet for us. | |
1055 | \ | |
1056 | : rcr-has-pkt? ( -- pkt-waiting? ) | |
1057 | chan# process-receive-errors | |
1058 | ||
1059 | 1 chan# rcr-flsh-i! \ Force HW to flash latest info to DRAM | |
1060 | \ But test shows that we do not have to flush. | |
1061 | ||
1062 | chan# rcrstat-a-i@ | |
1063 | ; | |
1064 | ||
1065 | \ Check where the packet is and how long the packet is. | |
1066 | \ | |
1067 | : get-pkt-addr&len-from-rcr ( -- nextrcdaddr pkt-cpu-adr pktlen ) | |
1068 | \ Get packet address and length | |
1069 | nextrcd@ ( nextrcd-cpu-addr ) | |
1070 | dup dup | |
1071 | \ In the rcd what we get is the RBR(pkt)'s io addr. | |
1072 | \ rcdaddr>pkt-cpu-adr converts the io address to cpu address | |
1073 | rcdaddr>pkt-cpu-adr ( nextrcd-cpu-addr nextrcd-cpu-addr pkt-cpu-adr ) | |
1074 | swap ( nextrcd-cpu-addr pkt-cpu-adr nextrcd-cpu-addr ) | |
1075 | rcdaddr>pkt-len ( nextrcd-cpu-addr pkt-cpu-adr pkt-len ) | |
1076 | ||
1077 | \ Sync contents of Rx packet buffer before looking at it | |
1078 | \ Note that this DMA sync is for packet buffer (whose address was obtained | |
1079 | \ from RCR entry) rather than for the RCR entry itself. rcr-has-pkt? has | |
1080 | \ done DMA sync for RCR entry with synciopb. | |
1081 | 2dup ( nextrcd-cpu-addr pkt-cpu-adr pkt-len pkt-cpu-adr pkt-len ) | |
1082 | sync-buf ( nextrcd-cpu-addr pkt-cpu-adr pkt-len ) | |
1083 | ; | |
1084 | ||
1085 | : send-wait ( chan -- ok? ) | |
1086 | d# 4000 get-msecs + false ( chan out-time false ) | |
1087 | begin | |
1088 | over ( chan out-time f out-time ) | |
1089 | timed-out? ( chan out-time f T/F ) | |
1090 | 0= ( chan out-time f F/T ) | |
1091 | over ( chan out-time f F/T f ) | |
1092 | 0= ( chan out-time f F/T t ) | |
1093 | and ( chan out-time f F/T ) \ F/T=F if TO | |
1094 | \ Enter while only if true ( not TO and f has not been replace by T ) | |
1095 | while ( chan out-time f ) | |
1096 | >r over ( chan out-time chan ) ( r: false ) | |
1097 | r> swap ( chan out-time false chan ) | |
1098 | dup tx-head@ ( chan out-time false chan head ) | |
1099 | over tx-tail@ ( chan out-time false chan head tail ) | |
1100 | = if ( chan out-time false chan ) | |
1101 | dup tx-hdr-wrap@ over tx-tail-wrap@ = if ( same as above ) | |
1102 | \ Check the MK bit of TX_CS register to double | |
1103 | \ check that the packet whose descriptor has the | |
1104 | \ MARK bit on has really been transmitted. This | |
1105 | \ read will set the MK bit to 0 | |
1106 | tx-cs-i@ mk and if ( chan out-time false ) | |
1107 | drop true ( chan out-time true ) | |
1108 | else | |
1109 | ." MK bit is not on" cr ( chan out-time false ) | |
1110 | then | |
1111 | else | |
1112 | . " Wrap mismatch" cr | |
1113 | drop \ Drop chan before going to begin | |
1114 | then | |
1115 | else ( chan out-time false chan ) | |
1116 | drop ( chan out-time false ) | |
1117 | then | |
1118 | repeat ( chan out-time f/t ) | |
1119 | -rot 2drop ( f/t ) | |
1120 | dup 0= if ( f/t ) | |
1121 | cmn-warn[ " Timeout waiting for Tx completion" ]cmn-end | |
1122 | true to restart? | |
1123 | then | |
1124 | ; | |
1125 | ||
1126 | ||
1127 | \ When this word is called, the application data has been copied | |
1128 | \ from application data buffer to the 4K Tx block buffer. | |
1129 | \ | |
1130 | : transmit ( tbuf-cpu-adr len -- ok? ) | |
1131 | \ Sync contents of the TX buffer first | |
1132 | 2dup sync-buf ( tbuf-cpu-adr len ) | |
1133 | ||
1134 | \ Initialize TX message descriptor (tmd). The tmd has two | |
1135 | \ parts, Bits above bit43 are for "header", which contain info | |
1136 | \ such as the length of the application data, start-of-frame | |
1137 | \ (SOF) indicator, etc. Bits[43:0] is for storing the io | |
1138 | \ address of the 4K Tx block buffer. tmd-init constructs | |
1139 | \ the tmd. | |
1140 | nexttmd@ ( tbuf-cpu-adr len tmdaddr ) | |
1141 | tmd-init ( len ) | |
1142 | ||
1143 | \ Calculate TAIL for kick. | |
1144 | \ in the PRM, TAIL of TX_RING_KICK register is specified as | |
1145 | \ an offset, in number of entries, from the staring address. | |
1146 | \ The following two lines figure out the offset. | |
1147 | nexttmd@ tmdaddr>tmd# 1 + ( len tmd#+1 ) | |
1148 | #tmds mod ( len tail ) \ tail=(tmd#+1)mod64 | |
1149 | ||
1150 | \ Because TAIL occupies bits[18:3] of TX_RING_KICK, left | |
1151 | \ shift the offset by 3. | |
1152 | dup dup ( len tail tail tail ) | |
1153 | d# 3 lshift swap ( len tail tail<<3 tail ) | |
1154 | ||
1155 | \ PRM requires that if we wrap around Tx Ring, we should turn | |
1156 | \ on the WRAP bit of TX_RING_KICK register | |
1157 | 0= ( len tail tail<<3 tail=0? ) | |
1158 | if ( len tail tail<<3 ) | |
1159 | wrap wrap=1 xor ( len tail tail<<3 WRAP ) | |
1160 | dup to wrap \ Save WRAP value for this round | |
1161 | else | |
1162 | wrap | |
1163 | then | |
1164 | or ( len tail WRAP||TAIL<<3 ) | |
1165 | \ Now kick the descriptor to chan#-th DMA channel to start | |
1166 | \ transmission. | |
1167 | ||
1168 | chan# tx-ring-kick-i! ( tail ) | |
1169 | ||
1170 | \ "tail" on top of the stack is actually the tmd index. | |
1171 | \ Convert it to tmdaddr (in cpu space) and save it in | |
1172 | \ nexttmd | |
1173 | tmd#>tmdaddr nexttmd! ( Empty ) \ Update nexttmd | |
1174 | ||
1175 | \ Wait for transmit completion | |
1176 | chan# send-wait ( ok? ) | |
1177 | ||
1178 | \ Check for transmit errors | |
1179 | transmit-errors? if | |
1180 | .transmit-errors clear-tx-errors drop false | |
1181 | then | |
1182 | ||
1183 | restart? if restart-net 2drop false then | |
1184 | ; | |
1185 | ||
1186 | \ ASIC group endorsed IPP init sequence: | |
1187 | \ o Set interrupt mask | |
1188 | \ o Configure IPP (Enable/Disable ECC Correct/CRC Drop/Cksum...) | |
1189 | \ o Set max packet size IPP can handle | |
1190 | \ o Initialize IPP counters | |
1191 | \ o Enble IPP | |
1192 | \ | |
1193 | : init-ipp ( -- ) | |
1194 | soft-rst port ipp-cfig-p! | |
1195 | \ ipp-intr-msk port ipp-msk-p! \ Mask off IPP interrupts | |
1196 | ||
1197 | \ Clear IPP counters by reading them | |
1198 | port ipp-pkt-dis-p@ drop | |
1199 | port ipp-bad-cs-cnt-p@ drop | |
1200 | port ipp-ecc-p@ drop | |
1201 | ||
1202 | \ Enable IPP | |
1203 | \ port ipp-cfig-p@ | |
1204 | ipp-max-pkt-default ipp-enable or | |
1205 | \ dfifo-ecc-en or \ Enable ecc detection and correction | |
1206 | \ drop-bad-crc or | |
1207 | \ chksum-en or \ Enable tcp/ip udp checksum | |
1208 | \ dfifo-pio-w or | |
1209 | \ pfifo-pio-w or | |
1210 | port ipp-cfig-p! | |
1211 | ; | |
1212 | ||
1213 | : (setup-link) ( -- link-up? ) | |
1214 | chan# reset-dma-channel 0= if | |
1215 | false exit ( flag ) | |
1216 | then | |
1217 | ||
1218 | init-ipp \ IPP is between MAC and DMA, it is used by MAC loopback | |
1219 | ||
1220 | portmode 1g-copper = if | |
1221 | mac-mode normal = ( flag ) | |
1222 | mac-mode qgc-ext-loopback = or if ( ) | |
1223 | \ 1G copper has no serdes to init. | |
1224 | \ ASIC engr said there is no need to setup xcvr for 1G copper | |
1225 | setup-transceiver ( flag ) | |
1226 | 0= if false exit then ( flag ) | |
1227 | link-up? 0= if ( ) | |
1228 | cmn-note[ " Link is down." ]cmn-end false exit | |
1229 | then ( ) | |
1230 | then ( ) | |
1231 | then ( ) | |
1232 | ||
1233 | portmode 10g-fiber = if ( ) | |
1234 | mac-mode normal = ( flag ) | |
1235 | mac-mode 2xgf-ext-loopback = or if ( ) | |
1236 | init-internal-serdes ( ) | |
1237 | setup-transceiver 0= if ( ) | |
1238 | false exit ( flag ) | |
1239 | then ( ) | |
1240 | link-up? 0= if ( ) | |
1241 | cmn-note[ " Link is down." ]cmn-end false exit | |
1242 | then ( ) | |
1243 | then ( ) | |
1244 | mac-mode serdes-ewrap-loopback = ( flag ) | |
1245 | mac-mode serdes-pad-loopback = or if ( ) | |
1246 | init-internal-serdes ( ) | |
1247 | then ( ) | |
1248 | then ( ) | |
1249 | ||
1250 | init-xif \ Set xmac mode (10G 1G, loopback etc) | |
1251 | init-pcs 0= if cmn-error[ " PCS block init failed." ]cmn-end false exit then | |
1252 | ||
1253 | \ PRM: "MAC software reset to clean up clock line glitches" | |
1254 | reset-mac 0= if cmn-error[ " MAC block reset failed." ]cmn-end false exit then | |
1255 | init-mac | |
1256 | ||
1257 | \ classification must be called after reset-mac. Otherwise | |
1258 | \ the hostinfo set by this function will be cleared by reset-mac. | |
1259 | classification | |
1260 | ||
1261 | chan# init-dma-channel | |
1262 | ||
1263 | enable-mac | |
1264 | enable-pcs | |
1265 | true | |
1266 | ; | |
1267 | ||
1268 | \ If (setup-link) does not throw, then ['] (setup-link) catch | |
1269 | \ returns a false (consider it as NO error) on top of the return value | |
1270 | \ of (setup-link), which is the link-up? flag. | |
1271 | \ If there is a throw (the only reason for (setup-link) to throw is | |
1272 | \ that the user has specified a parameter that is not supported by the | |
1273 | \ 1G-copper transceiver), then (setup-link) will gracefully abort | |
1274 | \ and put a non-zero error code on top of the stack. The caller will | |
1275 | \ quit after seeing a non-zero error code. | |
1276 | \ | |
1277 | : setup-link ( -- [ link-status ] error? ) | |
1278 | ['] (setup-link) catch | |
1279 | ; | |
1280 | ||
1281 | ||
1282 | : (restart-net) ( -- link-up? ) | |
1283 | false to restart? | |
1284 | clear-tx-errors clear-rx-errors | |
1285 | setup-link if \ A non-zero value returned by setup-link means it has | |
1286 | false \ caught a throw from check-phy-capability. In that | |
1287 | \ case, the link will never be up because the throw | |
1288 | \ implies that the user desired speed or duplex | |
1289 | \ is not supported. | |
1290 | then \ If setup-link has returned a 0, then just forward | |
1291 | ; \ the link-up? value (could be true or false) | |
1292 | \ returned by (setup-link) to the caller. | |
1293 | ||
1294 | ['] (restart-net) to restart-net | |
1295 | ||
1296 | : bringup-link ( -- ok? ) | |
1297 | d# 20000 get-msecs + false | |
1298 | begin | |
1299 | over timed-out? 0= over 0= and | |
1300 | while | |
1301 | setup-link if \ setup-link returns ( -- link-up? 0 ) if OK. | |
1302 | 2drop false exit \ If there is a throw, which implies that | |
1303 | \ bringing up the link is impossible, | |
1304 | \ then drop the top 2 data (which are the | |
1305 | \ expire time and the false) put on the stack | |
1306 | \ by this word and exit | |
1307 | then ( link-up? ) | |
1308 | ||
1309 | \ Check the link-up? flag put on the stack by setup-link | |
1310 | if | |
1311 | drop true \ link-up? is true, so we are done. | |
1312 | else \ link is not up yet, keep waiting | |
1313 | cmn-type[ " Retrying network initialization" ]cmn-end | |
1314 | then | |
1315 | repeat nip \ Nip the timeout value. | |
1316 | ; | |
1317 | ||
1318 | ||
1319 | \ Neptune does not have global reset. This word resets sub blocks one by one. | |
1320 | \ | |
1321 | : resetall ( -- ) | |
1322 | reset-transceiver drop | |
1323 | reset-mac drop | |
1324 | chan# reset-dma-channel drop | |
1325 | niu? if | |
1326 | chan# reset-tx-dma-channel drop | |
1327 | chan# reset-rx-dma-channel drop | |
1328 | then | |
1329 | disable-all-intrs | |
1330 | ; | |
1331 | ||
1332 | headerless | |
1333 | ||
1334 |