\ ========== Copyright Header Begin ========================================== \ \ Hypervisor Software File: qhtd.fth \ \ Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved. \ \ - Do no alter or remove copyright notices \ \ - Redistribution and use of this software in source and binary forms, with \ or without modification, are permitted provided that the following \ conditions are met: \ \ - Redistribution of source code must retain the above copyright notice, \ this list of conditions and the following disclaimer. \ \ - Redistribution in binary form must reproduce the above copyright notice, \ this list of conditions and the following disclaimer in the \ documentation and/or other materials provided with the distribution. \ \ Neither the name of Sun Microsystems, Inc. or the names of contributors \ may be used to endorse or promote products derived from this software \ without specific prior written permission. \ \ This software is provided "AS IS," without a warranty of any kind. \ ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, \ INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A \ PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN \ MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR \ ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR \ DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN \ OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR \ FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE \ DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, \ ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF \ SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. \ \ You acknowledge that this software is not designed, licensed or \ intended for use in the design, construction, operation or maintenance of \ any nuclear facility. \ \ ========== Copyright Header End ============================================ id: @(#)qhtd.fth 1.1 07/01/24 purpose: Data structures and manuipulation routines for EHCI USB Controller \ See license at end of file hex headers \ XXX Isochronous is not supported in the current version of the EHCI driver \ --------------------------------------------------------------------------- \ Data structures for this implementation of the EHCI USB Driver include: \ - qh-ptr pointer to the asynchronous list of QHs \ - framelist pointer to the Periodic Frame List \ - intr internal array of interrupts \ --------------------------------------------------------------------------- \ Constants common to most EHCI data structures 1 constant TERMINATE 0 constant TYP_ITD 2 constant TYP_QH 4 constant TYP_SITD 6 constant TYP_FSTN \ Pipe type 0 constant pt-ctrl 1 constant pt-bulk 2 constant pt-intr 3 constant pt-iso \ --------------------------------------------------------------------------- \ Periodic Frame List as defined by the EHCI Spec; 4-KB aligned \ \ Each entry is composed of: bit 0 TERMINATE \ bits 2:1 Pipe type \ bits 31:5 Frame List Link Pointer \ --------------------------------------------------------------------------- h# 1000 constant /align4kb d# 1024 dup constant #framelist \ # of entries in framelist 4 * constant /framelist \ Size of framelist 0 value framelist 0 value framelist-unaligned 0 value framelist-phys : framelist! ( n idx -- ) 4 * framelist + le-l! ; : init-framelist ( -- ) \ Allocate framelist /framelist /align4kb aligned-alloc ( unaligned virt ) swap to framelist-unaligned ( virt ) dup to framelist ( virt ) /framelist true dma-map-in to framelist-phys ( ) \ Initialize framelist #framelist 0 do TERMINATE i framelist! loop framelist-phys periodic! ; \ --------------------------------------------------------------------------- \ Internal interrupt list corresponding with the Frame List \ --------------------------------------------------------------------------- struct 4 field >intr-head 4 field >intr-tail dup constant /intr-entry #framelist * constant /intr \ Size of intr 0 value intr \ Internal array of interrupts : 'intr ( idx -- adr ) /intr-entry * intr + ; : intr-head@ ( idx -- adr ) 'intr >intr-head l@ ; : intr-head! ( adr idx -- ) 'intr >intr-head l! ; : intr-tail@ ( idx -- adr ) 'intr >intr-tail l@ ; : intr-tail! ( adr idx -- ) 'intr >intr-tail l! ; : init-intr ( -- ) /intr alloc-mem dup to intr \ Allocate intr /intr erase \ Initialize intr ; \ --------------------------------------------------------------------------- \ Queue Element Transfer Descriptor (qTD) as defined by the EHCI Spec; 32-byte aligned \ --------------------------------------------------------------------------- struct \ Beginning of qTD 4 field >hcqtd-next \ Next qTD pointer 4 field >hcqtd-next-alt \ Alternate next qTD pointer 4 field >hcqtd-token \ qTD token 4 field >hcqtd-bptr0 \ Buffer pointer 0 (4KB aligned) 4 field >hcqtd-bptr1 \ Buffer pointer 1 4 field >hcqtd-bptr2 \ Buffer pointer 2 4 field >hcqtd-bptr3 \ Buffer pointer 3 4 field >hcqtd-bptr4 \ Buffer pointer 4 4 5 * field >hcqtd-xbptrs \ 64-bit buffer pointer extensions dup constant /hcqtd \ Driver specific fields 4 field >qtd-phys \ Physical address of qTD 4 field >qtd-next \ Next qTD virtual address 4 field >qtd-buf \ Buffer virtual address 4 field >qtd-pbuf \ Buffer physical address 4 field >qtd-/buf \ Buffer length (per qTD) 4 field >qtd-/buf-all \ Buffer length (size of the entire buffer) \ Only the first qTD has the entire size of buffer \ For bulk and intr qTDs d# 32 round-up constant /qtd \ >hcqtd-token constants h# 0000.0000 constant TD_TOGGLE_DATA0 h# 8000.0000 constant TD_TOGGLE_DATA1 h# 8000.0000 constant TD_TOGGLE_MASK h# 0000.8000 constant TD_IOC h# 0000.0c00 constant TD_C_ERR_MASK h# 0000.0400 constant TD_C_ERR1 h# 0000.0800 constant TD_C_ERR2 h# 0000.0c00 constant TD_C_ERR3 h# 0000.0000 constant TD_PID_OUT h# 0000.0100 constant TD_PID_IN h# 0000.0200 constant TD_PID_SETUP h# 0000.00ff constant TD_STAT_MASK h# 0000.0080 constant TD_STAT_ACTIVE h# 0000.0040 constant TD_STAT_HALTED \ Babble, error count=0, STALL h# 0000.0020 constant TD_STAT_DBUFF \ Data buffer error h# 0000.0010 constant TD_STAT_BABBLE \ Babble h# 0000.0008 constant TD_STAT_XERR \ Timeout, CRC, bad pid, etc h# 0000.0004 constant TD_STAT_MISS_MF \ Missed micro-frame h# 0000.0000 constant TD_STAT_S_SPLIT \ Start split transaction h# 0000.0002 constant TD_STAT_C_SPLIT \ Complete split transaction h# 0000.0000 constant TD_STAT_OUT \ Do OUT h# 0000.0001 constant TD_STAT_PING \ Do ping h# 0000.0001 constant TD_STAT_SPLIT_ERR \ Periodic split transaction ERR : td-data>di-data ( n -- n' ) TD_TOGGLE_MASK and if 1 else 0 then ; : di-data>td-data ( n -- n' ) if TD_TOGGLE_DATA1 else TD_TOGGLE_DATA0 then ; \ --------------------------------------------------------------------------- \ Queue Head (QH) as defined by the EHCI Spec; 32-byte aligned \ --------------------------------------------------------------------------- struct \ Beginning of QH fields 4 field >hcqh-next \ QH horizontal link pointer 4 field >hcqh-endp-char \ Endpoint characteristics 4 field >hcqh-endp-cap \ Endpoint capabilities 4 field >hcqh-cur-pqtd \ Current transaction descriptor pointer /hcqtd field >hcqh-overlay \ Transfer overlay area dup constant /hcqh \ Driver specific fields 4 field >qh-phys \ QH's physical address 4 field >qh-next \ Next QH's virtual address 4 field >qh-prev \ Previous QH's virtual address 4 field >qh-unaligned \ QH's unaligned address 4 field >qh-size \ Size of QH+qTDs 4 field >qh-#qtds \ # of qTDs in the list d# 32 round-up constant /qh \ >hcqh-endp-char constants h# 0800.0000 constant QH_CTRL_ENDP h# 0000.8000 constant QH_HEAD h# 0000.4000 constant QH_TD_TOGGLE h# 0000.0080 constant QH_INACTIVE_NEXT h# 0000.0000 constant QH_TUNE_RL_HS h# 0000.0000 constant QH_TUNE_RL_TT \ >hcqh-endp-cap constants h# 4000.0000 constant QH_MULT1 h# 8000.0000 constant QH_MULT2 h# c000.0000 constant QH_MULT3 0 value qh-ptr \ Head of all QHs \ --------------------------------------------------------------------------- \ QH and TDs for bulk, control and interrupt operations. \ QH and its list of TDs are allocated as needed. \ --------------------------------------------------------------------------- : sync-qh ( qh -- ) dup >qh-phys l@ /hcqh dma-sync ; : sync-qtd ( qtd -- ) dup >qtd-phys l@ /hcqtd dma-sync ; : sync-qhqtds ( qh -- ) dup >qh-phys l@ over >qh-size l@ dma-sync ; : map-out-bptrs ( qtd -- ) dup >qtd-buf l@ over >qtd-pbuf l@ rot >qtd-/buf-all l@ hcd-map-out ; : init-qh ( qh.u,v,p len #qtds -- ) 3 pick >qh-#qtds l! ( qh.u,v,p len ) 2 pick >qh-size l! ( qh.u,v,p ) over >qh-phys l! ( qh.u,v ) TERMINATE 2 pick >hcqh-next le-l! ( qh.u,v ) >qh-unaligned l! ( ) ; : link-qtds ( qtd.v qtd.p #qtds -- ) 1- 0 ?do ( qtd.v qtd.p ) TERMINATE 2 pick >hcqtd-next-alt le-l! ( qtd.v qtd.p ) 2dup swap >qtd-phys l! ( qtd.v qtd.p ) /qtd + ( qtd.v qtd.p' ) 2dup swap >hcqtd-next le-l! ( qtd.v qtd.p ) swap dup /qtd + tuck swap >qtd-next l! ( qtd.p qtd.v' ) swap ( qtd.v qtd.p ) loop \ Fix up the last qTD over >qtd-phys l! ( qtd.v ) TERMINATE over >hcqtd-next le-l! ( qtd.v ) TERMINATE swap >hcqtd-next-alt le-l! ( ) ; : link-qhqtd ( qtd.p qh -- ) >hcqh-overlay tuck ( qh.overlay qtd.p qh.overlay ) >hcqtd-next le-l! ( qh.overlay ) TERMINATE over >hcqtd-next-alt le-l! ( qh.overlay) TD_TOGGLE_DATA1 TD_STAT_PING or swap >hcqtd-token le-l! ( ) ; : link-qhqtds ( qtd.v qtd.p #qtds qh -- ) 2 pick swap link-qhqtd ( qtd.v qtd.p #qtds ) \ Link QH to qTD link-qtds ( ) \ Link qTDs ; : alloc-qhqtds ( #qtds -- qh qtd ) dup >r /qtd * /qh + dup >r ( len ) ( R: #qtds len ) aligned32-alloc-map-in ( qh.u,v,p ) ( R: #qtds len ) over r@ erase ( qh.u,v,p ) ( R: #qtds ) 3dup r> r@ init-qh ( qh.u,v,p ) ( R: #qtds ) rot drop ( qh.v,p ) ( R: #qtds ) over /qh + dup -rot ( qh qtd qh.p qtd ) ( R: #qtds ) swap /qh + ( qh qtd qtd.v,p ) ( R: #qtds ) r> 4 pick link-qhqtds ( qh qtd ) ; : free-qhqtds ( qh -- ) >r ( R: qh ) r@ >qh-unaligned l@ ( qh.u ) ( R: qh ) r@ dup >qh-phys l@ ( qh.u,v,p ) ( R: qh ) r> >qh-size l@ ( qh.u,v,p size ) aligned32-free-map-out ( ) ; \ --------------------------------------------------------------------------- \ qTD Buffer Pointers management \ --------------------------------------------------------------------------- 5 constant #bptr \ There are 5 Buffer Pointers in qTD h# 1000 constant /bptr \ Size of buffer at each Buffer Pointer[i] /bptr 1- constant bptr-ofs-mask \ Current Offset mask bptr-ofs-mask invert constant bptr-mask \ Buffer Pointer mask /bptr #bptr * constant /maxbptrs \ Maximum size of transfer for a qTD /bptr #bptr 1- * constant /maxbptrs-1 \ Maximum size of 4 Buffer Pointers \ Determine the size of transfer for a qTD : cal-/bptr ( phys len -- /xfer ) over dup /bptr round-up = if ( phys len ) nip /maxbptrs min ( /xfer ) else swap bptr-ofs-mask and ( len len0 ) tuck - /maxbptrs-1 min + ( /xfer ) then ; \ Determine the number of Buffer Pointers necessary : cal-#bptr ( phys len -- #bptr ) dup 0= if nip exit then swap dup /bptr round-up swap - ?dup if ( len len0 ) - ( len-len0 ) /bptr round-up /bptr / 1+ ( #bptr ) else ( len ) /bptr round-up /bptr / ( #bptr ) then ; \ Determine the number of qTDs necessary for the entire transfer : cal-#qtd ( phys len -- #qtds ) dup 0= if nip exit then cal-#bptr #bptr /mod swap if 1+ then ; : fill-qtd-bptrs ( buf phys len qtd -- actual ) >r rot r@ >qtd-buf l! ( phys len ) ( R: qtd ) dup r@ >qtd-/buf-all l! ( phys len ) ( R: qtd ) over r@ >qtd-pbuf l! ( phys len ) ( R: qtd ) over swap cal-/bptr tuck ( actual phys actual ) ( R: qtd ) dup r@ >qtd-/buf l! ( actual phys actual ) ( R: qtd ) over swap cal-#bptr ( actual phys #bptr ) ( R: qtd ) r> swap 0 ?do ( actual phys qtd ) 2dup >hcqtd-bptr0 i 4 * + le-l! ( actual phys qtd ) swap /bptr + bptr-mask and swap ( actual phys' qtd ) loop 2drop ( actual ) ; \ --------------------------------------------------------------------------- \ Async scheduling \ --------------------------------------------------------------------------- : async-wait ( -- ) begin usbcmd@ h# 20 and 5 >> usbsts@ h# 8000 and d# 15 >> = until ; : enable-async ( phys -- ) asynclist! async-wait usbcmd@ h# 20 or usbcmd! async-wait ; : disable-async ( -- ) async-wait usbcmd@ h# 20 invert and usbcmd! async-wait ; : insert-qh ( qh -- ) >r qh-ptr if \ If there is another qh in the system, link the new qh to the existing \ qh head. qh-ptr r@ >qh-prev l! qh-ptr >qh-next l@ r@ >qh-next l! qh-ptr >hcqh-next le-l@ r@ >hcqh-next le-l! r@ qh-ptr >qh-next l! r@ >qh-phys l@ qh-ptr >hcqh-next le-l! r> sync-qhqtds qh-ptr sync-qh else \ If there is no qh in the system, link it to itself and mark it the \ head. r@ to qh-ptr r@ >hcqh-endp-char dup le-l@ QH_HEAD or swap le-l! r@ dup >qh-next l! r@ >qh-phys l@ dup TYP_QH or r@ >hcqh-next le-l! r> sync-qhqtds enable-async then ; : remove-qh ( qh -- ) dup >qh-next l@ over = if \ If qh is the only qh in the system, disable-async and exit drop disable-async 0 to qh-ptr else \ Otherwise, qh.prev points to qh.next, fix up reclamation bits. \ Ring doorbell, wait for answer. \ Free qh, make sure the qh-ptr is up-to-date. dup >qh-prev l@ ?dup if ( qh prev.qh ) over >hcqh-next le-l@ over >hcqh-next le-l! over >qh-next l@ swap >qh-next l! dup sync-qh dup >qh-next l@ qh-ptr <> if dup >qh-prev l@ swap >qh-next l@ >qh-prev l! else drop then else >qh-next l@ to qh-ptr qh-ptr >hcqh-endp-char dup le-l@ QH_HEAD or swap le-l! 0 qh-ptr >qh-prev l! qh-ptr sync-qh then ring-doorbell then ; \ --------------------------------------------------------------------------- \ Interrupt scheduling \ XXX Make it simple for now and igore interval and make it a fixed poll \ XXX interval. \ \ Empirically, the 4 ms poll interval works optimally with the usb keyboard. \ --------------------------------------------------------------------------- 0 value #intr d# 32 constant intr-interval \ 4 ms poll interval : #intr++ ( -- ) #intr 1+ to #intr ; : #intr-- ( -- ) #intr 1- to #intr ; : periodic-wait ( -- ) begin usbcmd@ h# 10 and 4 >> usbsts@ h# 4000 and d# 14 >> = until ; : enable-periodic ( -- ) periodic-wait usbcmd@ h# 10 or usbcmd! periodic-wait ; : disable-periodic ( -- ) periodic-wait usbcmd@ h# 10 invert and usbcmd! periodic-wait ; : (insert-intr-qh) ( qh idx -- ) dup >r ( qh idx ) ( R: idx ) intr-tail@ ?dup 0= if ( qh ) ( R: idx ) dup r@ intr-head! ( qh ) ( R: idx ) dup >qh-phys l@ TYP_QH or r@ framelist! ( qh ) ( R: idx ) else ( qh tail ) ( R: idx ) 2dup >qh-next l! ( qh tail ) ( R: idx ) over >qh-phys l@ TYP_QH or over >hcqh-next le-l! ( qh tail ) ( R: idx ) over >qh-prev l! ( qh ) ( R: idx ) then r> intr-tail! ( ) ; : insert-intr-qh ( qh speed interval -- ) drop ( qh speed ) speed-high = if h# 0020 else h# 1c01 then over >hcqh-endp-cap dup le-l@ rot or swap le-l! ( qh ) #framelist 0 do dup i (insert-intr-qh) intr-interval +loop drop #intr 0= if enable-periodic then #intr++ ; : (remove-intr-qh) ( qh idx -- ) >r ( qh ) ( R: idx ) dup >qh-prev l@ ?dup if over >qh-next l@ swap >qh-next l! then dup >qh-next l@ ?dup if over >qh-prev l@ swap >qh-prev l! then r@ intr-head@ over = if ( qh ) ( R: idx ) dup >qh-next l@ dup r@ intr-head! ( qh nqh ) ( R: idx ) ?dup if >qh-phys l@ TYP_QH or else TERMINATE then r@ framelist! ( qh ) ( R: idx ) then r@ intr-tail@ over = if ( qh ) ( R: idx ) dup >qh-prev l@ r@ intr-tail! ( qh ) ( R: idx ) then r> 2drop ; : remove-intr-qh ( qh -- ) #intr-- ( qh ) #framelist 0 do dup i (remove-intr-qh) intr-interval +loop drop #intr 0= if disable-periodic then ; \ --------------------------------------------------------------------------- \ Wait for a QH to be done and process any errors. \ \ When done? returns no error found yet, the caller should check if errors \ were found in the TDs. \ --------------------------------------------------------------------------- 0 value timeout : .qtd-error ( cc -- ) dup TD_STAT_HALTED and if " Stalled; " USB_ERR_STALL set-usb-error then dup TD_STAT_DBUFF and if " Data Buffer Error; " USB_ERR_DBUFERR set-usb-error then dup TD_STAT_BABBLE and if " Babble Detected; " USB_ERR_BABBLE set-usb-error then dup TD_STAT_XERR and if " CRC/Timeout/Bad PID; " USB_ERR_CRC set-usb-error then dup TD_STAT_MISS_MF and if " Missed Micro-frame; " USB_ERR_MICRO_FRAME set-usb-error then TD_STAT_SPLIT_ERR and if " Periodic split-x error; " USB_ERR_SPLIT set-usb-error then ; : qh-done? ( qh -- done? ) >hcqh-overlay ( olay ) dup >hcqtd-next le-l@ ( olay pnext ) swap >hcqtd-token le-l@ ( pnext token ) dup TD_STAT_HALTED and -rot ( halted? pnext token ) TD_STAT_ACTIVE and 0= swap ( halted? inactive? pnext ) TERMINATE = and ( halted? done? ) or ( done?' ) ; : done? ( qh -- usberr ) begin process-hc-status ( qh ) dup sync-qh ( qh ) dup qh-done? ?dup 0= if 1 ms timeout 1- dup to timeout 0= then until ( qh ) qh-done? 0= if " Timeout" USB_ERR_TIMEOUT set-usb-error then usb-error ; : error? ( qh -- usberr ) dup >hcqh-endp-char le-l@ d# 12 >> 3 and speed-high = if h# fc else h# fd then swap >hcqh-overlay >hcqtd-token le-l@ and ?dup if .qtd-error then usb-error ; : get-actual ( qtd #qtd -- actual ) 0 -rot 0 ?do ( actual qtd ) dup sync-qtd ( actual qtd ) dup >hcqtd-token le-l@ dup TD_STAT_ACTIVE and 0= if over >qtd-/buf l@ ( actual qtd token len ) swap d# 16 >> h# 7fff and - ( actual qtd len' ) rot + swap ( actual' qtd ) else drop ( actual qtd ) then >qtd-next l@ ( actual qtd ) loop drop ( qtd ) ; \ --------------------------------------------------------------------------- \ Allocate a dummy qh to be head of the queue to get around the fact that \ the VIA 2.0 controller does not stop async when told to. \ --------------------------------------------------------------------------- 0 value dummy-qh : alloc-dummy-qh ( -- ) dummy-qh 0= if 1 alloc-qhqtds ( qh qtd ) drop to dummy-qh TERMINATE dummy-qh >hcqh-overlay >hcqtd-next le-l! then dummy-qh insert-qh ; : free-dummy-qh ( -- ) dummy-qh ?dup if free-qhqtds 0 to dummy-qh then ; : ?alloc-dummy-qh ( -- ) 0 my-w@ h# 1106 ( VIA ) = if alloc-dummy-qh then ; : (init-extra) ( -- ) ?alloc-dummy-qh init-intr init-framelist ; ' (init-extra) to init-extra headers \ LICENSE_BEGIN \ Copyright (c) 2006 FirmWorks \ \ Permission is hereby granted, free of charge, to any person obtaining \ a copy of this software and associated documentation files (the \ "Software"), to deal in the Software without restriction, including \ without limitation the rights to use, copy, modify, merge, publish, \ distribute, sublicense, and/or sell copies of the Software, and to \ permit persons to whom the Software is furnished to do so, subject to \ the following conditions: \ \ The above copyright notice and this permission notice shall be \ included in all copies or substantial portions of the Software. \ \ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, \ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF \ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND \ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE \ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION \ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION \ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ \ LICENSE_END