original comment: +Wilson03172004,marked due to this pci host does not support MWI
[linux-2.4.git] / arch / cris / drivers / lpslave / e100lpslave.S
1 ;;
2 ;; Etrax100 slave network<->parport forwarder
3 ;;
4 ;; Copyright (c) 1999, 2000, 2001, 2002, 2003 Axis Communications AB
5 ;;
6 ;; We got 784 bytes (par loader size) to do DMA forwarding
7 ;; between DMA0/1 (ethernet) and DMA3/4 (par port 0 RX/1 TX)
8 ;;
9
10 #include <linux/config.h>
11 #if 0
12 #define ASSEMBLER_MACROS_ONLY
13 #endif
14 #include <asm/sv_addr_ag.h>
15
16 #define BUFSIZE 0x600
17
18         ;; R_IRQ_READ2
19
20 #define DMA1EOPBIT 3
21 #define DMA0EOPBIT 1
22 #define DMA3EOPBIT 7
23 #define DMA4DESCBIT 8
24
25         ;; R_IRQ_READ0
26
27 #define PAR0ECPCMDBIT 11
28
29         ;; get host CMDs
30
31 #include "e100lpslave.h"
32
33 start:
34         ;; disable interrupts. we are not going to use them at all.
35
36         di
37
38         ;; setup DMA connections and port configuration
39
40         movu.w  0x84, r0        ; DMA2/3/4/5 to par ports
41         move.d  r0, [R_GEN_CONFIG]
42
43         ;; setup port PA dirs and turn on the LED to show were alive
44
45         movu.w  0x0cfb, r0      ; PA2-PA3 out, PA2 inactive
46         move.d  r0, [R_PORT_PA_SET]
47
48         ;; enable MDIO output pin
49         moveq IO_STATE(R_NETWORK_MGM_CTRL, mdoe, enable), r0
50         move.d  r0, [R_NETWORK_MGM_CTRL]
51
52         ;; accept broadcast frames, and enable station address 0
53         moveq   IO_STATE(R_NETWORK_REC_CONFIG, broadcast, receive) | \
54                 IO_STATE(R_NETWORK_REC_CONFIG, ma0, enable), r0
55         move.d  r0, [R_NETWORK_REC_CONFIG]
56
57         ;; use MII CLK mode, and enable the controller
58         moveq   IO_STATE(R_NETWORK_GEN_CONFIG, phy, mii_clk) | \
59                 IO_STATE(R_NETWORK_GEN_CONFIG, enable, on), r0
60         move.d  r0, [R_NETWORK_GEN_CONFIG]
61
62         move.d  IO_STATE(R_PAR0_CONFIG, ioe,     noninv)    |  \
63                 IO_STATE(R_PAR0_CONFIG, iseli,   noninv)    |  \
64                 IO_STATE(R_PAR0_CONFIG, iautofd, noninv)    |  \
65                 IO_STATE(R_PAR0_CONFIG, istrb,   noninv)    |  \
66                 IO_STATE(R_PAR0_CONFIG, iinit,   noninv)    |  \
67                 IO_STATE(R_PAR0_CONFIG, iperr,   noninv)    |  \
68                 IO_STATE(R_PAR0_CONFIG, iack,    noninv)    |  \
69                 IO_STATE(R_PAR0_CONFIG, ibusy,   noninv)    |  \
70                 IO_STATE(R_PAR0_CONFIG, ifault,  noninv)    |  \
71                 IO_STATE(R_PAR0_CONFIG, isel,    noninv)    |  \
72                 IO_STATE(R_PAR0_CONFIG, dma, enable)        |  \
73                 IO_STATE(R_PAR0_CONFIG, rle_in, disable)    |  \
74                 IO_STATE(R_PAR0_CONFIG, rle_out, disable)   |  \
75                 IO_STATE(R_PAR0_CONFIG, enable, on)         |  \
76                 IO_STATE(R_PAR0_CONFIG, force, on)          |  \
77                 IO_STATE(R_PAR0_CONFIG, mode, ecp_rev), r0      ; Reverse ECP - PAR0 is RX
78
79         move.d  r0, [R_PAR0_CONFIG]
80
81         move.d  IO_STATE(R_PAR1_CONFIG, ioe,     noninv)    |  \
82                 IO_STATE(R_PAR1_CONFIG, iseli,   noninv)    |  \
83                 IO_STATE(R_PAR1_CONFIG, iautofd, noninv)    |  \
84                 IO_STATE(R_PAR1_CONFIG, istrb,   noninv)    |  \
85                 IO_STATE(R_PAR1_CONFIG, iinit,   noninv)    |  \
86                 IO_STATE(R_PAR1_CONFIG, iperr,   inv)       |  \
87                 IO_STATE(R_PAR1_CONFIG, iack,    noninv)    |  \
88                 IO_STATE(R_PAR1_CONFIG, ibusy,   noninv)    |  \
89                 IO_STATE(R_PAR1_CONFIG, ifault,  noninv)    |  \
90                 IO_STATE(R_PAR1_CONFIG, isel,    noninv)    |  \
91                 IO_STATE(R_PAR1_CONFIG, dma, enable)        |  \
92                 IO_STATE(R_PAR1_CONFIG, rle_in, disable)    |  \
93                 IO_STATE(R_PAR1_CONFIG, rle_out, disable)   |  \
94                 IO_STATE(R_PAR1_CONFIG, enable, on)         |  \
95                 IO_STATE(R_PAR1_CONFIG, force, on)          |  \
96                 IO_STATE(R_PAR1_CONFIG, mode, ecp_fwd), r0      ; Forward ECP - PAR1 is TX
97
98         move.d  r0, [R_PAR1_CONFIG]
99
100         moveq   IO_FIELD(R_PAR1_DELAY, setup, 0), r0    ; setup time of value * 160 + 20 == 20 ns
101         move.d  r0, [R_PAR1_DELAY]
102
103         ;; we got four descriptors, that can be active at the same time:
104         ;; 1) from network
105         ;; 2) to parport
106         ;; 3) from parport
107         ;; 4) to network
108         ;;
109         ;; we got four buffers, each can hold a max packet (we use 1536 bytes)
110         ;; buffers 1 and 2 are used from network to parport, while
111         ;; buffers 3 and 4 are used from parport to network.
112         ;; 
113         ;; a double buffering scheme is used, so that new data can be read
114         ;; into a buffer pair while the last data is written out from the
115         ;; last buffer. if the read buffer is done before the write buffer,
116         ;; the reading will halt until the writing is done, at which point
117         ;; writing starts from the newly read and reading can start with
118         ;; the newly written.
119         ;; 
120
121         move.d  R_DMA_CH0_FIRST, r1   ; we use this as base for subsequent DMA ops
122         moveq   IO_STATE(R_DMA_CH1_CMD, cmd, start), r6
123         move.d  FN1desc, r7
124         move.d  R_IRQ_READ0, r9
125
126         ;; start receiving from network
127
128         jsr     startdmaFPTN
129         jsr     startdmaFNTP
130
131         
132
133         ;; ------------------- MAIN LOOP
134
135         ;; IRQ bits:    parport rcv is par0_ecp_cmd, then dma3_eop
136         ;;              network rcv is dma1_eop
137         ;;              parport tx  is dma4_desc
138         ;;              network tx  is dma0_eop
139
140 mainloop:
141
142         ;; ------- first handle the parport -> network link
143
144         ;; check if we got something from the parport
145
146         move.d  [r9], r0        ; r0 <- *R_IRQ_READ0
147         btstq   PAR0ECPCMDBIT, r0
148         bpl     noparecp
149         nop
150
151         ;; ack it by reading PAR0_STATUS_DATA
152
153         move.d  [R_PAR0_STATUS_DATA], r0
154
155         ;; trigger EOP on DMA3 (par0 incoming channel)
156
157         moveq   IO_STATE(R_SET_EOP, ch3_eop, set), r0
158         move.d  r0, [R_SET_EOP]
159
160 noparecp:
161
162         ;; if we simultaneously have parport rx EOP and
163         ;; network TX eop, we can swap buffers and start a new RX/TX
164
165         move.d  [r9 + (R_IRQ_READ2 - R_IRQ_READ0)], r0
166         btstq   DMA3EOPBIT, r0  ; check parport rx
167         bpl     noswap1
168         btstq   DMA0EOPBIT, r0  ; check network tx
169         bpl     noswap1
170         nop
171
172         ;; prepare to swap buffer ptrs (FN3b <-> TN4b)
173
174         move.d  [r4 = r7 + 56], r0; FP3b
175         move.d  [r3 = r7 + 72], r2; TN4b
176
177         ;; but first check if this was a Host Command Packet
178
179         move.d  [r0], r5        ; r5 <- first 4 bytes in PAR-received packet
180         bne     handle_command  ; if non-zero, it was a host command
181         addq    4, r0           ; skip command (in delay slot - handle_command requires this)
182         move.d  r0, [r3]        ; write to To Network descriptor
183         subq    4, r2           ; undo the skipping done last swap
184         move.d  r2, [r4]        ; write to From Parport descriptor
185
186         ;; clear the interrupts
187
188         moveq   IO_STATE(R_DMA_CH0_CLR_INTR, clr_eop, do), r0
189         move.b  r0, [r1 + (R_DMA_CH0_CLR_INTR - R_DMA_CH0_FIRST)]
190         move.b  r0, [r1 + (R_DMA_CH3_CLR_INTR - R_DMA_CH0_FIRST)]
191
192         ;; copy received length to outgoing network length
193
194         move.w  [r7 + 60], r0   ; FPhlen
195         subq    4, r0           ; skip command
196         move.w  r0, [r7 + 64]   ; TN4desc
197
198         ;; restart DMAs
199
200         jsr     startdmaFPTN
201
202 #ifdef CONFIG_ETRAX_ETHERNET_LPSLAVE_HAS_LEDS
203 #if defined(CONFIG_ETRAX_NETWORK_LED_ON_WHEN_LINK)
204         ;; Turn off the LED signaling an outgoing network packet
205         movu.b  [LEDOff], r0
206 #elif defined(CONFIG_ETRAX_NETWORK_LED_ON_WHEN_ACTIVITY)
207         ;; Light the LED signaling an outgoing network packet
208         movu.b  [LEDAmber], r0
209 #else
210 #error "Define either CONFIG_ETRAX_NETWORK_LED_ON_WHEN_LINK or CONFIG_ETRAX_NETWORK_LED_ON_WHEN_ACTIVITY"       
211 #endif 
212         move.b  r0, [R_PORT_PA_DATA]
213         move.d  0x00011000, r0
214         move.d  r0,[LEDCount]
215 #endif
216
217 noswap1:
218         ;; ----- now check the network -> parport link
219
220
221         ;; if we simultaneously have network rx EOP and
222         ;; parport TX desc, we can swap buffers and start a new RX/TX
223
224         move.d  [r9 + (R_IRQ_READ2 - R_IRQ_READ0)], r0
225         btstq   DMA1EOPBIT, r0  ; check network rx
226         bpl     noswap2
227         btstq   DMA4DESCBIT, r0 ; check parport tx
228         bpl     noswap2
229         nop
230
231         ;; prepare to swap buffer ptrs (FP1b <-> TP2b)
232
233         move.d  [r4 = r7 +  8], r0; FN1b
234         move.d  [r3 = r7 + 24], r2; TP2b
235         move.d  r0, [r3]        ; write to To Parport descriptor
236         move.d  r2, [r4]        ; write to From Network descriptor
237
238         ;; clear the interrupts
239
240         moveq   IO_STATE(R_DMA_CH1_CLR_INTR, clr_eop, do) | \
241                 IO_STATE(R_DMA_CH1_CLR_INTR, clr_descr, do), r0
242         move.b  r0, [r1 + (R_DMA_CH1_CLR_INTR - R_DMA_CH0_FIRST)]
243         move.b  r0, [r1 + (R_DMA_CH4_CLR_INTR - R_DMA_CH0_FIRST)]
244
245         ;; copy received network length to outgoing parport length
246
247         move.w  [r7 + 12], r0   ; FNhlen
248         move.w  r0, [r7 + 16]   ; TP2desc
249
250         ;; restart DMAs
251
252         jsr     startdmaFNTP
253 #if 0
254 #ifdef CONFIG_ETRAX_ETHERNET_LPSLAVE_HAS_LEDS
255         ;; Light the LED signaling an incoming networkpacket
256         movu.b  0xFB, r0
257         move.b  r0, [R_PORT_PA_DATA]
258         move.d  0x00010000, r0
259         move.d  r0,[LEDCount]
260 #endif
261 #endif
262
263 noswap2:
264 #ifdef CONFIG_ETRAX_ETHERNET_LPSLAVE_HAS_LEDS
265
266         ;; Count down LED counter, and turn off the network LED if required
267         move.d  [LEDCount], r0
268         beq     mainloop
269         nop
270
271         subq    1, r0
272         move.d  r0, [LEDCount]  
273         bne     mainloop
274         nop
275
276 #if defined(CONFIG_ETRAX_NETWORK_LED_ON_WHEN_LINK)      
277         ;; Light the network LED , and start over the main loop
278         movu.b  [LEDAmber], r0
279 #elif defined(CONFIG_ETRAX_NETWORK_LED_ON_WHEN_ACTIVITY)                
280         ;; Turn off the network LED, and start over the main loop
281         movu.b  [LEDOff], r0
282 #else
283 #error "Define either CONFIG_ETRAX_NETWORK_LED_ON_WHEN_LINK or CONFIG_ETRAX_NETWORK_LED_ON_WHEN_ACTIVITY"       
284 #endif
285         move.b  r0, [R_PORT_PA_DATA]
286 #endif
287
288         ba      mainloop
289         nop
290
291         ;; --- some useful subroutines.
292
293 handle_command:
294         ;; handle command. we also need to clear the PAR0 RX EOP IRQ, and 
295         ;; restart the PAR0 dma. command is in R5, packet after cmd is in R0
296
297         moveq   IO_STATE(R_DMA_CH3_CLR_INTR, clr_eop, do), r2
298         move.b  r2, [r1 + (R_DMA_CH3_CLR_INTR - R_DMA_CH0_FIRST)]
299
300         cmpq    HOST_CMD_SETMAC, r5
301         bne     no_setmac
302         nop
303
304         ;; copy station address (6 bytes) from packet to hardware
305
306         move.d  [r0+], r2
307         move.d  R_NETWORK_SA_0, r3
308         move.d  r2, [r3]
309         move.w  [r0], r2
310         move.w  r2, [r3 + 4] 
311
312 no_setmac:
313         move    noswap1, SRP
314         ba      startdmaFP
315         nop
316
317         ;; start DMAs, from parport and to network
318
319 startdmaFPTN:
320
321         ;; start transmitting to the network (CH0)
322
323         move.d  TN4desc, r8
324         move.d  r8, [r1]                                        ; TN4desc -> FIRST0
325         move.b  r6, [r1 + (R_DMA_CH0_CMD - R_DMA_CH0_FIRST)]    ; start -> CMD0
326
327 startdmaFP:
328
329         ;; start receiving from parport (CH3)
330
331         move.d  FP3desc, r8
332         move.d  r8, [r1 + (R_DMA_CH3_FIRST - R_DMA_CH0_FIRST)]  ; FP3desc -> FIRST3
333         move.b  r6, [r1 + (R_DMA_CH3_CMD - R_DMA_CH0_FIRST)]    ; start -> CMD3
334
335         ret
336         nop
337
338         ;; start DMAs, from network and to parport
339
340 startdmaFNTP:
341
342         ;; start transmitting to the parport (CH4)
343
344         move.d  TP2desc, r8
345         move.d  r8, [r1 + (R_DMA_CH4_FIRST - R_DMA_CH0_FIRST)]  ; TP2desc -> FIRST4
346         move.b  r6, [r1 + (R_DMA_CH4_CMD - R_DMA_CH0_FIRST)]    ; start -> CMD4
347
348         ;; start receiving from network (CH1) (r7 already contains FN1desc)
349
350         move.d  r7, [r1 + (R_DMA_CH1_FIRST - R_DMA_CH0_FIRST)]  ; FN1desc -> FIRST1
351         move.b  r6, [r1 + (R_DMA_CH1_CMD - R_DMA_CH0_FIRST)]    ; start -> CMD1
352
353         ret
354         nop
355
356         ;; --- DMA descriptors - each descriptor is 4 longwords (16 bytes)
357         ;; DONT MOVE THESE AROUND. Due to the as/ld "hole-in-the-head",
358         ;; we cant write stuff like (TP2b - TP2desc) but the offsets
359         ;; have to be hardcoded.
360
361         .data
362
363         ;; 0 from network
364 FN1desc:
365         .word   BUFSIZE         ; sw_len
366         .word   0x0001          ; ctrl, d_eol is only flag we need
367         .dword  0               ; next
368 FN1b:   .dword  buffers         ; buffer 1 8
369         .word   0               ; hw_len
370         .word   0               ; status
371
372         ;; 16 to parport
373 TP2desc:
374         .word   2               ; sw_len, filled in by code 
375         .word   0x0004          ; ctrl, d_wait because ecp cmd in next
376         .dword  TP2desc2        ; next
377 TP2b:   .dword  buffers + BUFSIZE ; buffer 2 24
378         .word   0               ; hw_len
379         .word   0               ; status
380
381         ;; 32 to parport second descriptor, for the ECP command
382 TP2desc2:
383         .word   0x0001          ; sw_len, 1 byte (ecp command) 
384         .word   0x0019          ; ctrl, d_ecp | d_eol | d_int
385         .dword  0               ; next
386         .dword  TP2desc2        ; buffer, dont care
387         .word   0               ; hw_len
388         .word   0               ; status
389
390         ;; 48 from parport
391 FP3desc:
392         .word   BUFSIZE         ; sw_len
393         .word   0x0001          ; ctrl, d_eol is only flag we need
394         .dword  0               ; next
395 FP3b:   .dword  buffers + BUFSIZE * 2 ; 56 buffer 3
396 FPhlen: .word   0               ; 60 hw_len
397         .word   0               ; status
398
399         ;; 64 to network
400 TN4desc:
401         .word   2               ; sw_len, filled in by code 
402         .word   0x0007          ; ctrl, d_eop | d_eol | d_wait
403         .dword  0               ; next
404 TN4b:   .dword  buffers + BUFSIZE * 3 + 4       ; 72 buffer 4 (the +4 is to offset the anti-skipping)
405         .word   0               ; hw_len
406         .word   0               ; status
407
408 #ifdef CONFIG_ETRAX_ETHERNET_LPSLAVE_HAS_LEDS
409 LEDCount:
410         .dword  0
411 LEDOff: 
412         .word   0xff
413 LEDGreen:
414         .word   0xfb
415 LEDRed:
416         .word   0xf7
417 LEDAmber:
418         .word   0xf3
419 LED:
420         .word   0xf7
421 #endif
422
423         ;; after the prog we put the buffers. not in the asm program, we just use
424         ;; the address generated
425
426 buffers:
427
428         ;; END