Initial commit of OpenSPARC T2 architecture model.
[OpenSPARC-T2-SAM] / obp / obp / dev / usb / nq.fth
CommitLineData
920dae64
AT
1\ ========== Copyright Header Begin ==========================================
2\
3\ Hypervisor Software File: nq.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 ============================================
42id: @(#)nq.fth 1.4 99/09/21
43purpose:
44copyright: Copyright 1997-1999 Sun Microsystems, Inc. All Rights Reserved
45
46\ Look down the q until the next-endpoint is 0 (which will be on the last
47\ descriptor), or next-endpoint points to a dummy q (which will be on the
48\ last of one of the interrupt q's).
49: find-last-endpoint ( addr1 -- addr2 )
50 begin ( e-addr )
51 dup next-endpoint@ ( curr-addr nxt-addr )
52 dup if dev>virt dummy-endpoint? invert then ( e-addr use-next? )
53 while next-endpoint@ dev>virt
54 repeat
55;
56
57\ Stick new endpoints only on the end of the q. So even for the interrupt
58\ q's, any following endpoint does not need its prev-endpoint pointer changed,
59\ since it is head of queue and has prev = 0.
60\ So this endpoint descriptor and the previous one are the ones that must be
61\ synced.
62: e>q ( addr q-id -- ) \ endpoint to q-id
63 2dup swap q-id !
64 interrupt-dummy find-last-endpoint >r
65 dup r@ next-endpoint@ \ there are other q's, so
66 swap next-endpoint! \ copy next into this one
67 r> swap 2dup prev-endpoint ! \ hook this one in
68 2dup virt>dev swap next-endpoint! ( prev-ep cur-ep )
69 sync-endpoint sync-endpoint
70;
71
72\ XXX need to carry data toggle from endpoint descriptor forward from
73\ transfer descriptor to transfer descriptor? it is returned in the
74\ endpoint descriptor.
75\ No, let the transfer descriptor handle it -- which means the child
76\ device, essentially. This works for control transactions, but seems
77\ cumbersome for interrupt transactions.
78
79\ The endpoint descriptors must have at least a null transfer descriptor
80\ attached before putting the endpoint desc. on a queue to avoid a race
81\ condition when queueing real transfer descriptors.
82\ The last transfer descriptor is not touched by the chip (ohci 4.6).
83
84: make-transfer-d ( -- addr )
85 /transfer get-chunk
86;
87
88\ Assume no endpoint will be put on q unless it has a real transfer
89\ descriptor.
90\ endpoint direction from transfer descriptors
91: make-endpoint ( speed max-pkt endpt usb-addr -- addr )
92 /endpoint get-chunk >r
93 swap 7 lshift or swap h# 10 lshift or
94 swap if h# 2000 or then
95 r@ endpoint-control le-l! r>
96\ Make empty transfer-d and attach.
97 make-transfer-d virt>dev
98 tuck over td-tail! tuck td-head!
99;
100
101
102: transfer-bits>copy-in? ( transfer-d endp-bits -- copy-in? dummy )
103 swap transfer-control le-l@
104 d# 19 rshift 3 and \ dp bits
105 2 < \ setup or out
106 swap
107;
108
109: copy-in? ( transfer-d -- copy-in? )
110 dup my-endpoint @ endpoint-control le-l@
111 d# 11 rshift 3 and \ d bits
112 case 1 of drop true endof
113 2 of drop false endof
114 transfer-bits>copy-in?
115 endcase
116;
117
118\ for setup packets or out packets -- need to look at endp-d
119: copy-in ( transfer-d -- )
120 dup copy-in? if
121 dup caller-data @
122 over my-data @
123 rot caller-count @
124 move
125 else drop
126 then
127;
128
129: copy-for-me ( transfer-d -- )
130 dup caller-count @ ?dup if \ a real transfer
131 get-chunk over my-data ! \ make a copy buffer
132 copy-in
133 else drop
134 then
135;
136
137: my-dev-data ( transfer-d -- dev-data-adr )
138 my-data @ dup if virt>dev then
139;
140
141: buffer-bounds ( transfer-d -- )
142 dup my-dev-data over curr-buffer le-l!
143 dup my-dev-data over caller-count @ +
144 dup if 1- then \ may be a 0 len transfer
145 swap buffer-end le-l!
146;
147
148\ XXX don't allow caller-addr <>0 with caller-len = 0. the other way
149\ is ok (for interrupt transfers).
150\ bufferRounding, no DelayInterrupt
151: fill-transfer-d ( caller-data-adr buf-len toggle pid-code transfer-adr -- )
152 >r
153 d# 19 lshift h# 4.0000 or swap d# 24 lshift or
154 r@ transfer-control le-l! ( caller-addr len ) ( R: transfer-d )
155 r@ caller-count !
156 r@ caller-data !
157 r@ copy-for-me
158 r> buffer-bounds
159;
160
161
162\ buffer to hold offsets so they can be reversed; 100 nominal
163h# 100 buffer: isoc-temp \ must be global
164
165: make-isoc-offsets ( dev-badr blen -- offset7 ... offset0 )
166 isoc-temp h# 100 erase
167 isoc-temp 0 2swap ( buf-adr index dev-badr blen )
168 swap h# fff and swap \ only bottom 12 bits needed
169 bounds do ( buf-adr index )
170 2dup na+
171 i swap !
172 1+
173 d# 1023 +loop
174 drop ( buf-adr )
175 0 7 do
176 dup i na+ @ swap
177 -1 +loop ( off7 ... off0 buf-adr )
178 drop
179;
180
181: fill-isoc-offsets ( offset7 ... offset0 transfer-d -- )
182 4 0 do
183 >r
184 swap wljoin
185 r@ offset0 i 4 * + le-l!
186 r>
187 loop
188 drop
189;
190
191: (set-isoc-offsets) ( dev-badr blen transfer-d -- )
192 >r
193 dup d# 1023 /mod
194 swap if 1+ then
195 1- 7 and d# 24 lshift
196 r@ isoc-control tuck
197 le-l@ h# f8ff.ffff and or
198 swap le-l! ( dev-badr blen ) ( R: transfer-d )
199 make-isoc-offsets
200 r> fill-isoc-offsets \ fill in the offsets from offsets on stack
201;
202
203\ ohci 4.3.2.3.5.4 says max isoc data packet 3ff (1023) bytes, but the
204\ transfer descriptor seems to allow 400 (1024) bytes.
205\ uhci 3.2.3 says max len is 1023 also, and says it's in the usb spec.
206\ it also says that 1280 is the max theoretical for one frame.
207\ usb 5.6.3 says 1023 bytes max
208\ each page 4K, two pages max.
209: set-isoc-offsets ( dev-badr blen transfer-d -- )
210 over if (set-isoc-offsets) then
211;
212
213\ XXX isoc transfer direction seems to be in the endpoint, not the transfer
214\ no DelayInterrupt
215: fill-isoc-transfer-d ( f# caller-d dev-badr blen transfer-d -- )
216 >r r@ /transfer erase
217 over h# ffff.f000 and
218 r@ buff-page le-l!
219 2dup + over if 1- then \ could be 0 len transfer
220 r@ buffer-end le-l!
221 r@ set-isoc-offsets ( f# caller-d ) ( R: transfer-d )
222 r@ caller-data !
223 h# ffff and \ only least 16 bits of frame used
224 r> isoc-control tuck le-l@
225 h# ffff.0000 and
226 or swap le-l!
227;
228
229
230\ The data gets copied into the last transfer descriptor, a new empty
231\ descriptor gets hooked to it, then the tail pointer is updated to point
232\ to the new empty one.
233
234\ transfer to endpoint q:
235: t>endq ( caller-data-adr buf-len toggle pid-code endpoint-addr -- )
236
237 \ Find last transfer d on q:
238 >r r@ td-tail@ dev>virt ( ... tail-trans-d-addr )
239 r@ over my-endpoint !
240
241 \ Put transfer info into the last one on the q:
242 dup >r fill-transfer-d r> ( tail-d-addr ) ( R: endpt-addr )
243
244 make-transfer-d ( tail-addr new-last-addr )
245
246 \ Hook new one into old tail d:
247 virt>dev tuck swap next-transfer le-l! ( tail-addr new-last new-dev-addr )
248 1 r@ transfer-count +! \ bump transfer count
249 r> td-tail! \ Update td-tail in the endpoint d
250 sync-mem
251;
252
253\ XXX no dev-badr
254\ isoc transfer to endpoint q:
255: isoct>endq ( f# caller-d dev-badr blen endp-d -- )
256 >r r@ td-tail@ dev>virt ( ... tail-transfer-d )
257 dup >r fill-isoc-transfer-d ( -- ) ( R: endp-d tail-d )
258 r> dup r@ swap my-endpoint !
259 make-transfer-d ( tail-addr new-last-addr )
260
261 \ Hook new one into old tail d:
262 virt>dev tuck swap next-transfer le-l! ( tail-addr new-last new-dev-addr )
263 1 r@ transfer-count +! \ bump transfer count
264 r> td-tail! \ Update td-tail in the endpoint d
265\ XXX should fill my-endpoint for the new last descriptor.
266 sync-mem
267;
268
269
270\ Two alternative strategies:
271
272\ 1. Each request from child node gets essentially one endpoint with a string
273\ of transfers. when transfer finishes, retire the endpoint.
274\ 2. the same endpoint can be used for more than one request from child node.
275
276\ I lean towards 1. This means that it probably makes most sense for the
277\ endpoint to have all its transfer d's installed before putting the endpoint
278\ on the q.
279
280
281\ : low-bits? ( n high-bit -- low-bits? )
282\ xor
283\ ;
284
285: find-high-bit ( n1 -- n2 )
286 dup h# 20 and if drop h# 20 exit then
287 dup h# 10 and if drop h# 10 exit then
288 dup 8 and if drop 8 exit then
289 dup 4 and if drop 4 exit then
290 2 and if 2 else 1 then
291;
292
293\ : next-power-of-two ( n -- power )
294\ dup find-high-bit
295\ tuck low-bits? if 1+ then
296\ ;
297
298: prev-power-of-two ( n -- power )
299 find-high-bit
300;
301
302\ Use power to find the correct range of q's. Maybe use ms to "hash" into
303\ the range for the specific q. Should also take into account the loading
304\ of the various q's.
305\ XXX Stupid algorithm, pick the last q of the right priority.
306\ It always works:
307: choose-q ( ms power -- [q#] q-found? )
308 nip d# 63 swap -
309 true
310;
311
312\ Pick the q to use based on ms, the maximum polling interval, and the
313\ loading of the various q's with that interval.
314\ q-found? is non-false if it was able to pick a q. If q-found? is non-false,
315\ q# is returned also.
316\ Use semi-brute-force: The numbers are only up to 32, so for the valid
317\ ones, 1) mask for the high bit, 2) check for any low bits, and 3) if there
318\ are low bits, double the high bit. This gets to the next greater
319\ power-of-two.
320\ Previous algorithm uses minimum polling interval. For maximum polling
321\ interval, semi-brute-force: 1) mask for the high bit. This gets to the
322\ previous power-of-two.
323: pick-q ( ms -- [q#] q-found? )
324 dup 0<= over d# 32 > or if \ out of range
325 drop 0
326 else
327 dup prev-power-of-two ( ms power )
328 choose-q
329 then
330;
331
332\ XXX There should be a bandwidth check when adding and deleting transfers and
333\ endpoints. And picking q's?