head 1.24; access; symbols netbsd-11-0-RC4:1.24 netbsd-11-0-RC3:1.24 netbsd-11-0-RC2:1.24 netbsd-11-0-RC1:1.24 perseant-exfatfs-base-20250801:1.24 netbsd-11:1.24.0.4 netbsd-11-base:1.24 netbsd-10-1-RELEASE:1.23 perseant-exfatfs-base-20240630:1.24 perseant-exfatfs:1.24.0.2 perseant-exfatfs-base:1.24 netbsd-8-3-RELEASE:1.19 netbsd-9-4-RELEASE:1.20 netbsd-10-0-RELEASE:1.23 netbsd-10-0-RC6:1.23 netbsd-10-0-RC5:1.23 netbsd-10-0-RC4:1.23 netbsd-10-0-RC3:1.23 netbsd-10-0-RC2:1.23 thorpej-ifq:1.23.0.8 thorpej-ifq-base:1.23 thorpej-altq-separation:1.23.0.6 thorpej-altq-separation-base:1.23 netbsd-10-0-RC1:1.23 netbsd-10:1.23.0.4 netbsd-10-base:1.23 bouyer-sunxi-drm:1.23.0.2 bouyer-sunxi-drm-base:1.23 netbsd-9-3-RELEASE:1.20 thorpej-i2c-spi-conf2:1.20.0.24 thorpej-i2c-spi-conf2-base:1.20 thorpej-futex2:1.20.0.22 thorpej-futex2-base:1.20 thorpej-cfargs2:1.20.0.20 thorpej-cfargs2-base:1.20 cjep_sun2x-base1:1.20 cjep_sun2x:1.20.0.18 cjep_sun2x-base:1.20 cjep_staticlib_x-base1:1.20 netbsd-9-2-RELEASE:1.20 cjep_staticlib_x:1.20.0.16 cjep_staticlib_x-base:1.20 thorpej-i2c-spi-conf:1.20.0.14 thorpej-i2c-spi-conf-base:1.20 thorpej-cfargs:1.20.0.12 thorpej-cfargs-base:1.20 thorpej-futex:1.20.0.10 thorpej-futex-base:1.20 netbsd-9-1-RELEASE:1.20 bouyer-xenpvh-base2:1.20 phil-wifi-20200421:1.20 bouyer-xenpvh-base1:1.20 phil-wifi-20200411:1.20 bouyer-xenpvh:1.20.0.8 bouyer-xenpvh-base:1.20 is-mlppp:1.20.0.6 is-mlppp-base:1.20 phil-wifi-20200406:1.20 netbsd-8-2-RELEASE:1.19 ad-namecache-base3:1.20 netbsd-9-0-RELEASE:1.20 netbsd-9-0-RC2:1.20 ad-namecache-base2:1.20 ad-namecache-base1:1.20 ad-namecache:1.20.0.4 ad-namecache-base:1.20 netbsd-9-0-RC1:1.20 phil-wifi-20191119:1.20 netbsd-9:1.20.0.2 netbsd-9-base:1.20 phil-wifi-20190609:1.20 netbsd-8-1-RELEASE:1.19 netbsd-8-1-RC1:1.19 isaki-audio2:1.19.0.24 isaki-audio2-base:1.19 pgoyette-compat-merge-20190127:1.19 pgoyette-compat-20190127:1.19 pgoyette-compat-20190118:1.19 pgoyette-compat-1226:1.19 pgoyette-compat-1126:1.19 pgoyette-compat-1020:1.19 pgoyette-compat-0930:1.19 pgoyette-compat-0906:1.19 jdolecek-ncqfixes:1.19.0.22 jdolecek-ncqfixes-base:1.19 netbsd-7-2-RELEASE:1.18 pgoyette-compat-0728:1.19 netbsd-8-0-RELEASE:1.19 phil-wifi:1.19.0.20 phil-wifi-base:1.19 pgoyette-compat-0625:1.19 netbsd-8-0-RC2:1.19 pgoyette-compat-0521:1.19 pgoyette-compat-0502:1.19 pgoyette-compat-0422:1.19 netbsd-8-0-RC1:1.19 pgoyette-compat-0415:1.19 pgoyette-compat-0407:1.19 pgoyette-compat-0330:1.19 pgoyette-compat-0322:1.19 pgoyette-compat-0315:1.19 netbsd-7-1-2-RELEASE:1.18 pgoyette-compat:1.19.0.18 pgoyette-compat-base:1.19 netbsd-7-1-1-RELEASE:1.18 tls-maxphys-base-20171202:1.19 matt-nb8-mediatek:1.19.0.16 matt-nb8-mediatek-base:1.19 nick-nhusb-base-20170825:1.19 perseant-stdc-iso10646:1.19.0.14 perseant-stdc-iso10646-base:1.19 netbsd-8:1.19.0.12 netbsd-8-base:1.19 prg-localcount2-base3:1.19 prg-localcount2-base2:1.19 prg-localcount2-base1:1.19 prg-localcount2:1.19.0.10 prg-localcount2-base:1.19 pgoyette-localcount-20170426:1.19 bouyer-socketcan-base1:1.19 jdolecek-ncq:1.19.0.8 jdolecek-ncq-base:1.19 pgoyette-localcount-20170320:1.19 netbsd-7-1:1.18.0.122 netbsd-7-1-RELEASE:1.18 netbsd-7-1-RC2:1.18 nick-nhusb-base-20170204:1.19 netbsd-7-nhusb-base-20170116:1.18 bouyer-socketcan:1.19.0.6 bouyer-socketcan-base:1.19 pgoyette-localcount-20170107:1.19 netbsd-7-1-RC1:1.18 nick-nhusb-base-20161204:1.19 pgoyette-localcount-20161104:1.19 netbsd-7-0-2-RELEASE:1.18 nick-nhusb-base-20161004:1.19 localcount-20160914:1.19 netbsd-7-nhusb:1.18.0.120 netbsd-7-nhusb-base:1.18 pgoyette-localcount-20160806:1.19 pgoyette-localcount-20160726:1.19 pgoyette-localcount:1.19.0.4 pgoyette-localcount-base:1.19 nick-nhusb-base-20160907:1.19 nick-nhusb-base-20160529:1.19 netbsd-7-0-1-RELEASE:1.18 nick-nhusb-base-20160422:1.19 nick-nhusb-base-20160319:1.19 nick-nhusb-base-20151226:1.19 netbsd-7-0:1.18.0.118 netbsd-7-0-RELEASE:1.18 nick-nhusb-base-20150921:1.19 netbsd-7-0-RC3:1.18 netbsd-7-0-RC2:1.18 netbsd-7-0-RC1:1.18 nick-nhusb-base-20150606:1.19 nick-nhusb-base-20150406:1.19 nick-nhusb:1.19.0.2 nick-nhusb-base:1.19 netbsd-5-2-3-RELEASE:1.18 netbsd-5-1-5-RELEASE:1.18 netbsd-6-0-6-RELEASE:1.18 netbsd-6-1-5-RELEASE:1.18 netbsd-7:1.18.0.116 netbsd-7-base:1.18 yamt-pagecache-base9:1.18 yamt-pagecache-tag8:1.18 netbsd-6-1-4-RELEASE:1.18 netbsd-6-0-5-RELEASE:1.18 tls-earlyentropy:1.18.0.114 tls-earlyentropy-base:1.18 riastradh-xf86-video-intel-2-7-1-pre-2-21-15:1.18 riastradh-drm2-base3:1.18 netbsd-6-1-3-RELEASE:1.18 netbsd-6-0-4-RELEASE:1.18 netbsd-5-2-2-RELEASE:1.18 netbsd-5-1-4-RELEASE:1.18 netbsd-6-1-2-RELEASE:1.18 netbsd-6-0-3-RELEASE:1.18 netbsd-5-2-1-RELEASE:1.18 netbsd-5-1-3-RELEASE:1.18 rmind-smpnet-nbase:1.18 netbsd-6-1-1-RELEASE:1.18 riastradh-drm2-base2:1.18 riastradh-drm2-base1:1.18 riastradh-drm2:1.18.0.112 riastradh-drm2-base:1.18 rmind-smpnet:1.18.0.104 rmind-smpnet-base:1.18 netbsd-6-1:1.18.0.110 netbsd-6-0-2-RELEASE:1.18 netbsd-6-1-RELEASE:1.18 khorben-n900:1.18.0.108 netbsd-6-1-RC4:1.18 netbsd-6-1-RC3:1.18 agc-symver:1.18.0.106 agc-symver-base:1.18 netbsd-6-1-RC2:1.18 netbsd-6-1-RC1:1.18 yamt-pagecache-base8:1.18 netbsd-5-2:1.18.0.102 netbsd-6-0-1-RELEASE:1.18 yamt-pagecache-base7:1.18 netbsd-5-2-RELEASE:1.18 netbsd-5-2-RC1:1.18 matt-nb6-plus-nbase:1.18 yamt-pagecache-base6:1.18 netbsd-6-0:1.18.0.100 netbsd-6-0-RELEASE:1.18 netbsd-6-0-RC2:1.18 tls-maxphys:1.18.0.98 tls-maxphys-base:1.18 matt-nb6-plus:1.18.0.96 matt-nb6-plus-base:1.18 netbsd-6-0-RC1:1.18 jmcneill-usbmp-base10:1.18 yamt-pagecache-base5:1.18 jmcneill-usbmp-base9:1.18 yamt-pagecache-base4:1.18 jmcneill-usbmp-base8:1.18 jmcneill-usbmp-base7:1.18 jmcneill-usbmp-base6:1.18 jmcneill-usbmp-base5:1.18 jmcneill-usbmp-base4:1.18 jmcneill-usbmp-base3:1.18 jmcneill-usbmp-pre-base2:1.18 jmcneill-usbmp-base2:1.18 netbsd-6:1.18.0.94 netbsd-6-base:1.18 netbsd-5-1-2-RELEASE:1.18 netbsd-5-1-1-RELEASE:1.18 jmcneill-usbmp:1.18.0.92 jmcneill-usbmp-base:1.18 jmcneill-audiomp3:1.18.0.90 jmcneill-audiomp3-base:1.18 yamt-pagecache-base3:1.18 yamt-pagecache-base2:1.18 yamt-pagecache:1.18.0.88 yamt-pagecache-base:1.18 rmind-uvmplock-nbase:1.18 cherry-xenmp:1.18.0.86 cherry-xenmp-base:1.18 bouyer-quota2-nbase:1.18 bouyer-quota2:1.18.0.84 bouyer-quota2-base:1.18 jruoho-x86intr:1.18.0.82 jruoho-x86intr-base:1.18 matt-mips64-premerge-20101231:1.18 matt-nb5-mips64-premerge-20101231:1.18 matt-nb5-pq3:1.18.0.80 matt-nb5-pq3-base:1.18 netbsd-5-1:1.18.0.78 netbsd-5-1-RELEASE:1.18 uebayasi-xip-base4:1.18 uebayasi-xip-base3:1.18 yamt-nfs-mp-base11:1.18 netbsd-5-1-RC4:1.18 matt-nb5-mips64-k15:1.18 uebayasi-xip-base2:1.18 yamt-nfs-mp-base10:1.18 netbsd-5-1-RC3:1.18 netbsd-5-1-RC2:1.18 uebayasi-xip-base1:1.18 netbsd-5-1-RC1:1.18 rmind-uvmplock:1.18.0.76 rmind-uvmplock-base:1.18 yamt-nfs-mp-base9:1.18 uebayasi-xip:1.18.0.74 uebayasi-xip-base:1.18 netbsd-5-0-2-RELEASE:1.18 matt-nb5-mips64-premerge-20091211:1.18 matt-premerge-20091211:1.18 yamt-nfs-mp-base8:1.18 matt-nb5-mips64-u2-k2-k4-k7-k8-k9:1.18 matt-nb4-mips64-k7-u2a-k9b:1.18 matt-nb5-mips64-u1-k1-k5:1.18 yamt-nfs-mp-base7:1.18 matt-nb5-mips64:1.18.0.72 netbsd-5-0-1-RELEASE:1.18 jymxensuspend-base:1.18 yamt-nfs-mp-base6:1.18 yamt-nfs-mp-base5:1.18 yamt-nfs-mp-base4:1.18 jym-xensuspend-nbase:1.18 yamt-nfs-mp-base3:1.18 nick-hppapmap-base4:1.18 nick-hppapmap-base3:1.18 netbsd-5-0:1.18.0.70 netbsd-5-0-RELEASE:1.18 netbsd-5-0-RC4:1.18 netbsd-5-0-RC3:1.18 nick-hppapmap-base2:1.18 netbsd-5-0-RC2:1.18 jym-xensuspend:1.18.0.68 jym-xensuspend-base:1.18 netbsd-5-0-RC1:1.18 haad-dm-base2:1.18 haad-nbase2:1.18 ad-audiomp2:1.18.0.66 ad-audiomp2-base:1.18 netbsd-5:1.18.0.64 netbsd-5-base:1.18 nick-hppapmap:1.18.0.62 nick-hppapmap-base:1.18 matt-mips64-base2:1.18 matt-mips64:1.18.0.60 haad-dm-base1:1.18 wrstuden-revivesa-base-4:1.18 netbsd-4-0-1-RELEASE:1.18 wrstuden-revivesa-base-3:1.18 wrstuden-revivesa-base-2:1.18 wrstuden-fixsa-newbase:1.18 nick-csl-alignment-base5:1.18 haad-dm:1.18.0.58 haad-dm-base:1.18 wrstuden-revivesa-base-1:1.18 simonb-wapbl-nbase:1.18 yamt-pf42-base4:1.18 simonb-wapbl:1.18.0.56 simonb-wapbl-base:1.18 yamt-pf42-base3:1.18 hpcarm-cleanup-nbase:1.18 yamt-pf42-baseX:1.18 yamt-pf42-base2:1.18 yamt-nfs-mp-base2:1.18 wrstuden-revivesa:1.18.0.54 wrstuden-revivesa-base:1.18 yamt-nfs-mp:1.18.0.52 yamt-nfs-mp-base:1.18 yamt-pf42:1.18.0.50 yamt-pf42-base:1.18 ad-socklock-base1:1.18 yamt-lazymbuf-base15:1.18 yamt-lazymbuf-base14:1.18 keiichi-mipv6-nbase:1.18 mjf-devfs2:1.18.0.48 mjf-devfs2-base:1.18 nick-net80211-sync:1.18.0.46 nick-net80211-sync-base:1.18 keiichi-mipv6:1.18.0.44 keiichi-mipv6-base:1.18 bouyer-xeni386-merge1:1.18 matt-armv6-prevmlocking:1.18 wrstuden-fixsa-base-1:1.18 vmlocking2-base3:1.18 netbsd-4-0:1.18.0.42 netbsd-4-0-RELEASE:1.18 bouyer-xeni386-nbase:1.18 yamt-kmem-base3:1.18 cube-autoconf:1.18.0.40 cube-autoconf-base:1.18 yamt-kmem-base2:1.18 bouyer-xeni386:1.18.0.38 bouyer-xeni386-base:1.18 yamt-kmem:1.18.0.36 yamt-kmem-base:1.18 vmlocking2-base2:1.18 reinoud-bufcleanup-nbase:1.18 vmlocking2:1.18.0.34 vmlocking2-base1:1.18 netbsd-4-0-RC5:1.18 matt-nb4-arm:1.18.0.32 matt-nb4-arm-base:1.18 matt-armv6-nbase:1.18 jmcneill-base:1.18 netbsd-4-0-RC4:1.18 mjf-devfs:1.18.0.30 mjf-devfs-base:1.18 bouyer-xenamd64-base2:1.18 vmlocking-nbase:1.18 yamt-x86pmap-base4:1.18 bouyer-xenamd64:1.18.0.28 bouyer-xenamd64-base:1.18 netbsd-4-0-RC3:1.18 yamt-x86pmap-base3:1.18 yamt-x86pmap-base2:1.18 netbsd-4-0-RC2:1.18 yamt-x86pmap:1.18.0.26 yamt-x86pmap-base:1.18 netbsd-4-0-RC1:1.18 matt-armv6:1.18.0.24 matt-armv6-base:1.18 matt-mips64-base:1.18 jmcneill-pm:1.18.0.22 jmcneill-pm-base:1.18 hpcarm-cleanup:1.18.0.20 hpcarm-cleanup-base:1.18 nick-csl-alignment:1.18.0.18 nick-csl-alignment-base:1.18 netbsd-3-1-1-RELEASE:1.16 netbsd-3-0-3-RELEASE:1.16 yamt-idlelwp-base8:1.18 wrstuden-fixsa:1.18.0.16 wrstuden-fixsa-base:1.18 thorpej-atomic:1.18.0.14 thorpej-atomic-base:1.18 reinoud-bufcleanup:1.18.0.12 reinoud-bufcleanup-base:1.18 mjf-ufs-trans:1.18.0.10 mjf-ufs-trans-base:1.18 vmlocking:1.18.0.8 vmlocking-base:1.18 ad-audiomp:1.18.0.6 ad-audiomp-base:1.18 yamt-idlelwp:1.18.0.4 post-newlock2-merge:1.18 newlock2-nbase:1.18 yamt-splraiseipl-base5:1.18 yamt-splraiseipl-base4:1.18 yamt-splraiseipl-base3:1.18 abandoned-netbsd-4-base:1.17 abandoned-netbsd-4:1.17.0.18 netbsd-3-1:1.16.0.14 netbsd-3-1-RELEASE:1.16 netbsd-3-0-2-RELEASE:1.16 yamt-splraiseipl-base2:1.17 netbsd-3-1-RC4:1.16 yamt-splraiseipl:1.17.0.22 yamt-splraiseipl-base:1.17 netbsd-3-1-RC3:1.16 yamt-pdpolicy-base9:1.17 newlock2:1.17.0.20 newlock2-base:1.18 yamt-pdpolicy-base8:1.17 netbsd-3-1-RC2:1.16 netbsd-3-1-RC1:1.16 yamt-pdpolicy-base7:1.17 netbsd-4:1.18.0.2 netbsd-4-base:1.18 yamt-pdpolicy-base6:1.17 chap-midi-nbase:1.17 netbsd-3-0-1-RELEASE:1.16 gdamore-uart:1.17.0.16 gdamore-uart-base:1.17 simonb-timcounters-final:1.17 yamt-pdpolicy-base5:1.17 chap-midi:1.17.0.14 chap-midi-base:1.17 yamt-pdpolicy-base4:1.17 yamt-pdpolicy-base3:1.17 peter-altq-base:1.17 peter-altq:1.17.0.12 yamt-pdpolicy-base2:1.17 elad-kernelauth-base:1.17 elad-kernelauth:1.17.0.10 yamt-pdpolicy:1.17.0.8 yamt-pdpolicy-base:1.17 yamt-uio_vmspace-base5:1.17 simonb-timecounters:1.17.0.6 simonb-timecounters-base:1.17 rpaulo-netinet-merge-pcb:1.17.0.4 rpaulo-netinet-merge-pcb-base:1.17 yamt-uio_vmspace:1.17.0.2 netbsd-3-0:1.16.0.12 netbsd-3-0-RELEASE:1.16 netbsd-3-0-RC6:1.16 yamt-readahead-base3:1.16 netbsd-3-0-RC5:1.16 netbsd-3-0-RC4:1.16 netbsd-3-0-RC3:1.16 yamt-readahead-base2:1.16 netbsd-3-0-RC2:1.16 yamt-readahead-pervnode:1.16 yamt-readahead-perfile:1.16 yamt-readahead:1.16.0.10 yamt-readahead-base:1.16 netbsd-3-0-RC1:1.16 yamt-vop-base3:1.16 netbsd-2-0-3-RELEASE:1.15 netbsd-2-1:1.15.0.16 yamt-vop-base2:1.16 thorpej-vnode-attr:1.16.0.8 thorpej-vnode-attr-base:1.16 netbsd-2-1-RELEASE:1.15 yamt-vop:1.16.0.6 yamt-vop-base:1.16 netbsd-2-1-RC6:1.15 netbsd-2-1-RC5:1.15 netbsd-2-1-RC4:1.15 netbsd-2-1-RC3:1.15 netbsd-2-1-RC2:1.15 netbsd-2-1-RC1:1.15 yamt-lazymbuf:1.16.0.4 yamt-km-base4:1.16 netbsd-2-0-2-RELEASE:1.15 yamt-km-base3:1.16 netbsd-3:1.16.0.2 netbsd-3-base:1.16 yamt-km-base2:1.15 yamt-km:1.15.0.12 yamt-km-base:1.15 kent-audio2:1.15.0.10 kent-audio2-base:1.16 netbsd-2-0-1-RELEASE:1.15 kent-audio1-beforemerge:1.15 netbsd-2:1.15.0.8 netbsd-2-base:1.15 kent-audio1:1.15.0.6 kent-audio1-base:1.15 netbsd-2-0-RELEASE:1.15 netbsd-2-0-RC5:1.15 netbsd-2-0-RC4:1.15 netbsd-2-0-RC3:1.15 netbsd-2-0-RC2:1.15 netbsd-2-0-RC1:1.15 netbsd-2-0:1.15.0.4 netbsd-2-0-base:1.15 netbsd-1-6-PATCH002-RELEASE:1.13 netbsd-1-6-PATCH002:1.13 netbsd-1-6-PATCH002-RC4:1.13 netbsd-1-6-PATCH002-RC3:1.13 netbsd-1-6-PATCH002-RC2:1.13 netbsd-1-6-PATCH002-RC1:1.13 ktrace-lwp:1.15.0.2 ktrace-lwp-base:1.16 netbsd-1-6-PATCH001:1.13 netbsd-1-6-PATCH001-RELEASE:1.13 netbsd-1-6-PATCH001-RC3:1.13 netbsd-1-6-PATCH001-RC2:1.13 netbsd-1-6-PATCH001-RC1:1.13 nathanw_sa_end:1.11.6.2 nathanw_sa_before_merge:1.13 fvdl_fs64_base:1.13 gmcgarry_ctxsw:1.13.0.18 gmcgarry_ctxsw_base:1.13 gmcgarry_ucred:1.13.0.16 gmcgarry_ucred_base:1.13 nathanw_sa_base:1.13 kqueue-aftermerge:1.13 kqueue-beforemerge:1.13 netbsd-1-6-RELEASE:1.13 netbsd-1-6-RC3:1.13 netbsd-1-6-RC2:1.13 netbsd-1-6-RC1:1.13 netbsd-1-6:1.13.0.14 netbsd-1-6-base:1.13 gehenna-devsw:1.13.0.12 gehenna-devsw-base:1.13 netbsd-1-5-PATCH003:1.11 eeh-devprop:1.13.0.10 eeh-devprop-base:1.13 newlock:1.13.0.8 newlock-base:1.13 ifpoll-base:1.13 thorpej-mips-cache:1.13.0.4 thorpej-mips-cache-base:1.13 thorpej-devvp-base3:1.13 thorpej-devvp-base2:1.13 post-chs-ubcperf:1.13 pre-chs-ubcperf:1.13 thorpej-devvp:1.13.0.2 thorpej-devvp-base:1.13 netbsd-1-5-PATCH002:1.11 kqueue:1.12.0.2 kqueue-base:1.13 netbsd-1-5-PATCH001:1.11 thorpej_scsipi_beforemerge:1.11 nathanw_sa:1.11.0.6 thorpej_scsipi_nbase:1.11 netbsd-1-5-RELEASE:1.11 netbsd-1-5-BETA2:1.11 netbsd-1-5-BETA:1.11 netbsd-1-4-PATCH003:1.10 netbsd-1-5-ALPHA2:1.11 netbsd-1-5:1.11.0.4 netbsd-1-5-base:1.11 minoura-xpg4dl-base:1.11 minoura-xpg4dl:1.11.0.2 netbsd-1-4-PATCH002:1.10 chs-ubc2-newbase:1.10 wrstuden-devbsize-19991221:1.10 wrstuden-devbsize:1.10.0.20 wrstuden-devbsize-base:1.10 kame_141_19991130:1.10 comdex-fall-1999:1.10.0.18 comdex-fall-1999-base:1.10 fvdl-softdep:1.10.0.16 fvdl-softdep-base:1.10 thorpej_scsipi:1.10.0.14 thorpej_scsipi_base:1.11 netbsd-1-4-PATCH001:1.10 kame_14_19990705:1.10 kame_14_19990628:1.10 kame:1.10.0.12 chs-ubc2:1.10.0.10 chs-ubc2-base:1.10 netbsd-1-4-RELEASE:1.10 netbsd-1-4:1.10.0.8 netbsd-1-4-base:1.10 netbsd-1-3-PATCH003:1.6 netbsd-1-3-PATCH003-CANDIDATE2:1.6 kenh-if-detach:1.10.0.6 kenh-if-detach-base:1.10 netbsd-1-3-PATCH003-CANDIDATE1:1.6 netbsd-1-3-PATCH003-CANDIDATE0:1.6 chs-ubc:1.10.0.4 chs-ubc-base:1.10 eeh-paddr_t:1.10.0.2 eeh-paddr_t-base:1.10 netbsd-1-3-PATCH002:1.6 netbsd-1-3-PATCH001:1.6 netbsd-1-3-RELEASE:1.6 netbsd-1-3-BETA:1.6 netbsd-1-3:1.6.0.14 netbsd-1-3-base:1.6 thorpej-signal:1.6.0.12 thorpej-signal-base:1.6 marc-pcmcia:1.6.0.10 marc-pcmcia-bp:1.6 marc-pcmcia-base:1.6 bouyer-scsipi:1.6.0.8 is-newarp-before-merge:1.6 is-newarp:1.6.0.6 is-newarp-base:1.6 netbsd-1-2-PATCH001:1.3.4.2 mrg-vm-swap:1.6.0.4 thorpej-setroot:1.6.0.2 netbsd-1-2-RELEASE:1.3.4.1 netbsd-1-2-BETA:1.3 netbsd-1-2:1.3.0.4 netbsd-1-2-base:1.3 date-03-may-96:1.1.1.1 netbsd:1.1.1 netbsd-1-1-PATCH001:1.1 netbsd-1-1-RELEASE:1.1 netbsd-1-1:1.1.0.2 netbsd-1-1-base:1.1; locks; strict; comment @# @; 1.24 date 2024.02.10.08.36.04; author andvar; state Exp; branches; next 1.23; commitid 7fk0iLBOy4ZeBRXE; 1.23 date 2022.06.27.22.41.29; author andvar; state Exp; branches; next 1.22; commitid NZUyP4RB0ph0lJJD; 1.22 date 2022.01.25.22.01.34; author andvar; state Exp; branches; next 1.21; commitid uadEc5exKQRF14qD; 1.21 date 2021.09.03.22.33.17; author andvar; state Exp; branches; next 1.20; commitid gXr0XdzPsZd0Py7D; 1.20 date 2019.06.04.10.15.22; author msaitoh; state Exp; branches; next 1.19; commitid Ei516LpSmvd6bRpB; 1.19 date 2014.10.18.08.33.28; author snj; state Exp; branches 1.19.20.1; next 1.18; 1.18 date 2006.11.25.16.48.32; author christos; state Exp; branches 1.18.98.1; next 1.17; 1.17 date 2005.12.11.12.22.18; author christos; state Exp; branches 1.17.20.1 1.17.22.1; next 1.16; 1.16 date 2005.02.27.00.27.23; author perry; state Exp; branches 1.16.4.1; next 1.15; 1.15 date 2003.05.03.18.11.31; author wiz; state Exp; branches 1.15.2.1 1.15.10.1 1.15.12.1; next 1.14; 1.14 date 2003.04.19.19.26.10; author fvdl; state Exp; branches; next 1.13; 1.13 date 2001.08.20.12.00.52; author wiz; state Exp; branches; next 1.12; 1.12 date 2001.06.19.13.45.57; author wiz; state Exp; branches 1.12.2.1; next 1.11; 1.11 date 2000.03.15.02.09.11; author fvdl; state Exp; branches 1.11.6.1; next 1.10; 1.10 date 98.07.04.22.29.46; author mjacob; state Exp; branches 1.10.14.1; next 1.9; 1.9 date 98.07.03.23.45.10; author mjacob; state Exp; branches; next 1.8; 1.8 date 98.04.16.07.07.37; author leo; state Exp; branches; next 1.7; 1.7 date 98.03.11.20.57.03; author leo; state Exp; branches; next 1.6; 1.6 date 96.10.08.03.04.06; author gibbs; state Exp; branches; next 1.5; 1.5 date 96.08.10.08.36.58; author mycroft; state Exp; branches; next 1.4; 1.4 date 96.07.10.22.51.23; author explorer; state Exp; branches; next 1.3; 1.3 date 96.05.20.00.48.45; author thorpej; state Exp; branches 1.3.4.1; next 1.2; 1.2 date 96.05.16.03.51.45; author mycroft; state Exp; branches; next 1.1; 1.1 date 95.10.09.09.49.37; author mycroft; state Exp; branches; next ; 1.19.20.1 date 2019.06.10.22.07.13; author christos; state Exp; branches; next ; commitid jtc8rnCzWiEEHGqB; 1.18.98.1 date 2017.12.03.11.37.05; author jdolecek; state Exp; branches; next ; commitid XcIYRZTAh1LmerhA; 1.17.20.1 date 2007.01.12.00.57.38; author ad; state Exp; branches; next ; 1.17.22.1 date 2006.12.10.07.17.36; author yamt; state Exp; branches; next ; 1.16.4.1 date 2006.12.30.20.48.35; author yamt; state Exp; branches; next ; 1.15.2.1 date 2005.03.04.16.43.50; author skrll; state Exp; branches; next ; 1.15.10.1 date 2005.04.29.11.28.56; author kent; state Exp; branches; next ; 1.15.12.1 date 2005.03.19.08.34.42; author yamt; state Exp; branches; next ; 1.12.2.1 date 2001.08.25.06.16.19; author thorpej; state Exp; branches; next ; 1.11.6.1 date 2001.06.21.20.04.10; author nathanw; state Exp; branches; next 1.11.6.2; 1.11.6.2 date 2001.08.24.00.09.54; author nathanw; state Exp; branches; next ; 1.10.14.1 date 2000.11.20.11.41.33; author bouyer; state Exp; branches; next ; 1.3.4.1 date 96.07.18.00.38.01; author jtc; state Exp; branches; next 1.3.4.2; 1.3.4.2 date 97.03.04.14.55.23; author mycroft; state Exp; branches; next ; desc @@ 1.24 log @s/psuedo/pseudo/ in comments. @ text @/* * Adaptec 274x/284x/294x device driver firmware for Linux and FreeBSD. * * Copyright (c) 1994-2001 Justin T. Gibbs. * Copyright (c) 2000-2001 Adaptec Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions, and the following disclaimer, * without modification. * 2. Redistributions in binary form must reproduce at minimum a disclaimer * substantially similar to the "NO WARRANTY" disclaimer below * ("Disclaimer") and any redistribution must be conditioned upon * including a substantially similar Disclaimer requirement for further * binary redistribution. * 3. Neither the names of the above-listed copyright holders nor the names * of any contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * Alternatively, this software may be distributed under the terms of the * GNU General Public License ("GPL") version 2 as published by the Free * Software Foundation. * * NO WARRANTY * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGES. * * $FreeBSD: /repoman/r/ncvs/src/sys/dev/aic7xxx/aic7xxx.seq,v 1.123 2003/01/20 20:44:55 gibbs Exp $ */ VERSION = "$NetBSD: aic7xxx.seq,v 1.23 2022/06/27 22:41:29 andvar Exp $" PATCH_ARG_LIST = "struct ahc_softc *ahc" PREFIX = "ahc_" #include #include /* * A few words on the waiting SCB list: * After starting the selection hardware, we check for reconnecting targets * as well as for our selection to complete just in case the reselection wins * bus arbitration. The problem with this is that we must keep track of the * SCB that we've already pulled from the QINFIFO and started the selection * on just in case the reselection wins so that we can retry the selection at * a later time. This problem cannot be resolved by holding a single entry * in scratch ram since a reconnecting target can request sense and this will * create yet another SCB waiting for selection. The solution used here is to * use byte 27 of the SCB as a pseudo-next pointer and to thread a list * of SCBs that are awaiting selection. Since 0-0xfe are valid SCB indexes, * SCB_LIST_NULL is 0xff which is out of range. An entry is also added to * this list everytime a request sense occurs or after completing a non-tagged * command for which a second SCB has been queued. The sequencer will * automatically consume the entries. */ bus_free_sel: /* * Turn off the selection hardware. We need to reset the * selection request in order to perform a new selection. */ and SCSISEQ, TEMODE|ENSELI|ENRSELI|ENAUTOATNP; and SIMODE1, ~ENBUSFREE; poll_for_work: call clear_target_state; and SXFRCTL0, ~SPIOEN; if ((ahc->features & AHC_ULTRA2) != 0) { clr SCSIBUSL; } test SCSISEQ, ENSELO jnz poll_for_selection; if ((ahc->features & AHC_TWIN) != 0) { xor SBLKCTL,SELBUSB; /* Toggle to the other bus */ test SCSISEQ, ENSELO jnz poll_for_selection; } cmp WAITING_SCBH,SCB_LIST_NULL jne start_waiting; poll_for_work_loop: if ((ahc->features & AHC_TWIN) != 0) { xor SBLKCTL,SELBUSB; /* Toggle to the other bus */ } test SSTAT0, SELDO|SELDI jnz selection; test_queue: /* Has the driver posted any work for us? */ BEGIN_CRITICAL; if ((ahc->features & AHC_QUEUE_REGS) != 0) { test QOFF_CTLSTA, SCB_AVAIL jz poll_for_work_loop; } else { mov A, QINPOS; cmp KERNEL_QINPOS, A je poll_for_work_loop; } mov ARG_1, NEXT_QUEUED_SCB; /* * We have at least one queued SCB now and we don't have any * SCBs in the list of SCBs awaiting selection. Allocate a * card SCB for the host's SCB and get to work on it. */ if ((ahc->flags & AHC_PAGESCBS) != 0) { mov ALLZEROS call get_free_or_disc_scb; } else { /* In the non-paging case, the SCBID == hardware SCB index */ mov SCBPTR, ARG_1; } or SEQ_FLAGS2, SCB_DMA; END_CRITICAL; dma_queued_scb: /* * DMA the SCB from host ram into the current SCB location. */ mvi DMAPARAMS, HDMAEN|DIRECTION|FIFORESET; mov ARG_1 call dma_scb; /* * Check one last time to see if this SCB was canceled * before we completed the DMA operation. If it was, * the QINFIFO next pointer will not match our saved * value. */ mov A, ARG_1; BEGIN_CRITICAL; cmp NEXT_QUEUED_SCB, A jne abort_qinscb; if ((ahc->flags & AHC_SEQUENCER_DEBUG) != 0) { cmp SCB_TAG, A je . + 2; mvi SCB_MISMATCH call set_seqint; } mov NEXT_QUEUED_SCB, SCB_NEXT; mov SCB_NEXT,WAITING_SCBH; mov WAITING_SCBH, SCBPTR; if ((ahc->features & AHC_QUEUE_REGS) != 0) { mov NONE, SNSCB_QOFF; } else { inc QINPOS; } and SEQ_FLAGS2, ~SCB_DMA; END_CRITICAL; start_waiting: /* * Start the first entry on the waiting SCB list. */ mov SCBPTR, WAITING_SCBH; call start_selection; poll_for_selection: /* * Twin channel devices cannot handle things like SELTO * interrupts on the "background" channel. So, while * selecting, keep polling the current channel until * either a selection or reselection occurs. */ test SSTAT0, SELDO|SELDI jz poll_for_selection; selection: /* * We aren't expecting a bus free, so interrupt * the kernel driver if it happens. */ mvi CLRSINT1,CLRBUSFREE; if ((ahc->features & AHC_DT) == 0) { or SIMODE1, ENBUSFREE; } /* * Guard against a bus free after (re)selection * but prior to enabling the busfree interrupt. SELDI * and SELDO will be cleared in that case. */ test SSTAT0, SELDI|SELDO jz bus_free_sel; test SSTAT0,SELDO jnz select_out; select_in: if ((ahc->flags & AHC_TARGETROLE) != 0) { if ((ahc->flags & AHC_INITIATORROLE) != 0) { test SSTAT0, TARGET jz initiator_reselect; } mvi CLRSINT0, CLRSELDI; /* * We've just been selected. Assert BSY and * setup the phase for receiving messages * from the target. */ mvi SCSISIGO, P_MESGOUT|BSYO; /* * Setup the DMA for sending the identify and * command information. */ mvi SEQ_FLAGS, CMDPHASE_PENDING; mov A, TQINPOS; if ((ahc->features & AHC_CMD_CHAN) != 0) { mvi DINDEX, CCHADDR; mvi SHARED_DATA_ADDR call set_32byte_addr; mvi CCSCBCTL, CCSCBRESET; } else { mvi DINDEX, HADDR; mvi SHARED_DATA_ADDR call set_32byte_addr; mvi DFCNTRL, FIFORESET; } /* Initiator that selected us */ and SAVED_SCSIID, SELID_MASK, SELID; /* The Target ID we were selected at */ if ((ahc->features & AHC_MULTI_TID) != 0) { and A, OID, TARGIDIN; } else if ((ahc->features & AHC_ULTRA2) != 0) { and A, OID, SCSIID_ULTRA2; } else { and A, OID, SCSIID; } or SAVED_SCSIID, A; if ((ahc->features & AHC_TWIN) != 0) { test SBLKCTL, SELBUSB jz . + 2; or SAVED_SCSIID, TWIN_CHNLB; } if ((ahc->features & AHC_CMD_CHAN) != 0) { mov CCSCBRAM, SAVED_SCSIID; } else { mov DFDAT, SAVED_SCSIID; } /* * If ATN isn't asserted, the target isn't interested * in talking to us. Go directly to bus free. * XXX SCSI-1 may require us to assume lun 0 if * ATN is false. */ test SCSISIGI, ATNI jz target_busfree; /* * Watch ATN closely now as we pull in messages from the * initiator. We follow the guidlines from section 6.5 * of the SCSI-2 spec for what messages are allowed when. */ call target_inb; /* * Our first message must be one of IDENTIFY, ABORT, or * BUS_DEVICE_RESET. */ test DINDEX, MSG_IDENTIFYFLAG jz host_target_message_loop; /* Store for host */ if ((ahc->features & AHC_CMD_CHAN) != 0) { mov CCSCBRAM, DINDEX; } else { mov DFDAT, DINDEX; } and SAVED_LUN, MSG_IDENTIFY_LUNMASK, DINDEX; /* Remember for disconnection decision */ test DINDEX, MSG_IDENTIFY_DISCFLAG jnz . + 2; /* XXX Honor per target settings too */ or SEQ_FLAGS, NO_DISCONNECT; test SCSISIGI, ATNI jz ident_messages_done; call target_inb; /* * If this is a tagged request, the tagged message must * immediately follow the identify. We test for a valid * tag message by seeing if it is >= MSG_SIMPLE_Q_TAG and * < MSG_IGN_WIDE_RESIDUE. */ add A, -MSG_SIMPLE_Q_TAG, DINDEX; jnc ident_messages_done_msg_pending; add A, -MSG_IGN_WIDE_RESIDUE, DINDEX; jc ident_messages_done_msg_pending; /* Store for host */ if ((ahc->features & AHC_CMD_CHAN) != 0) { mov CCSCBRAM, DINDEX; } else { mov DFDAT, DINDEX; } /* * If the initiator doesn't feel like providing a tag number, * we've got a failed selection and must transition to bus * free. */ test SCSISIGI, ATNI jz target_busfree; /* * Store the tag for the host. */ call target_inb; if ((ahc->features & AHC_CMD_CHAN) != 0) { mov CCSCBRAM, DINDEX; } else { mov DFDAT, DINDEX; } mov INITIATOR_TAG, DINDEX; or SEQ_FLAGS, TARGET_CMD_IS_TAGGED; ident_messages_done: /* Terminate the ident list */ if ((ahc->features & AHC_CMD_CHAN) != 0) { mvi CCSCBRAM, SCB_LIST_NULL; } else { mvi DFDAT, SCB_LIST_NULL; } or SEQ_FLAGS, TARG_CMD_PENDING; test SEQ_FLAGS2, TARGET_MSG_PENDING jnz target_mesgout_pending; test SCSISIGI, ATNI jnz target_mesgout_continue; jmp target_ITloop; ident_messages_done_msg_pending: or SEQ_FLAGS2, TARGET_MSG_PENDING; jmp ident_messages_done; /* * Pushed message loop to allow the kernel to * run its own target mode message state engine. */ host_target_message_loop: mvi HOST_MSG_LOOP call set_seqint; cmp RETURN_1, EXIT_MSG_LOOP je target_ITloop; test SSTAT0, SPIORDY jz .; jmp host_target_message_loop; } if ((ahc->flags & AHC_INITIATORROLE) != 0) { /* * Reselection has been initiated by a target. Make a note that we've been * reselected, but haven't seen an IDENTIFY message from the target yet. */ initiator_reselect: /* XXX test for and handle ONE BIT condition */ or SXFRCTL0, SPIOEN|CLRSTCNT|CLRCHN; and SAVED_SCSIID, SELID_MASK, SELID; if ((ahc->features & AHC_ULTRA2) != 0) { and A, OID, SCSIID_ULTRA2; } else { and A, OID, SCSIID; } or SAVED_SCSIID, A; if ((ahc->features & AHC_TWIN) != 0) { test SBLKCTL, SELBUSB jz . + 2; or SAVED_SCSIID, TWIN_CHNLB; } mvi CLRSINT0, CLRSELDI; jmp ITloop; } abort_qinscb: call add_scb_to_free_list; jmp poll_for_work_loop; start_selection: /* * If bus reset interrupts have been disabled (from a previous * reset), re-enable them now. Resets are only of interest * when we have outstanding transactions, so we can safely * defer re-enabling the interrupt until, as an initiator, * we start sending out transactions again. */ test SIMODE1, ENSCSIRST jnz . + 3; mvi CLRSINT1, CLRSCSIRSTI; or SIMODE1, ENSCSIRST; if ((ahc->features & AHC_TWIN) != 0) { and SINDEX,~SELBUSB,SBLKCTL;/* Clear channel select bit */ test SCB_SCSIID, TWIN_CHNLB jz . + 2; or SINDEX, SELBUSB; mov SBLKCTL,SINDEX; /* select channel */ } initialize_scsiid: if ((ahc->features & AHC_ULTRA2) != 0) { mov SCSIID_ULTRA2, SCB_SCSIID; } else if ((ahc->features & AHC_TWIN) != 0) { and SCSIID, TWIN_TID|OID, SCB_SCSIID; } else { mov SCSIID, SCB_SCSIID; } if ((ahc->flags & AHC_TARGETROLE) != 0) { mov SINDEX, SCSISEQ_TEMPLATE; test SCB_CONTROL, TARGET_SCB jz . + 2; or SINDEX, TEMODE; mov SCSISEQ, SINDEX ret; } else { mov SCSISEQ, SCSISEQ_TEMPLATE ret; } /* * Initialize transfer settings with SCB provided settings. */ set_transfer_settings: if ((ahc->features & AHC_ULTRA) != 0) { test SCB_CONTROL, ULTRAENB jz . + 2; or SXFRCTL0, FAST20; } /* * Initialize SCSIRATE with the appropriate value for this target. */ if ((ahc->features & AHC_ULTRA2) != 0) { bmov SCSIRATE, SCB_SCSIRATE, 2 ret; } else { mov SCSIRATE, SCB_SCSIRATE ret; } if ((ahc->flags & AHC_TARGETROLE) != 0) { /* * We carefully toggle SPIOEN to allow us to return the * message byte we receive so it can be checked prior to * driving REQ on the bus for the next byte. */ target_inb: /* * Drive REQ on the bus by enabling SCSI PIO. */ or SXFRCTL0, SPIOEN; /* Wait for the byte */ test SSTAT0, SPIORDY jz .; /* Prevent our read from triggering another REQ */ and SXFRCTL0, ~SPIOEN; /* Save latched contents */ mov DINDEX, SCSIDATL ret; } /* * After the selection, remove this SCB from the "waiting SCB" * list. This is achieved by simply moving our "next" pointer into * WAITING_SCBH. Our next pointer will be set to null the next time this * SCB is used, so don't bother with it now. */ select_out: /* Turn off the selection hardware */ and SCSISEQ, TEMODE|ENSELI|ENRSELI|ENAUTOATNP, SCSISEQ; mov SCBPTR, WAITING_SCBH; mov WAITING_SCBH,SCB_NEXT; mov SAVED_SCSIID, SCB_SCSIID; mov SAVED_LUN, SCB_LUN; call set_transfer_settings; if ((ahc->flags & AHC_TARGETROLE) != 0) { test SSTAT0, TARGET jz initiator_select; or SXFRCTL0, CLRSTCNT|CLRCHN; /* * Put tag in connonical location since not * all connections have an SCB. */ mov INITIATOR_TAG, SCB_TARGET_ITAG; /* * We've just re-selected an initiator. * Assert BSY and setup the phase for * sending our identify messages. */ mvi P_MESGIN|BSYO call change_phase; mvi CLRSINT0, CLRSELDO; /* * Start out with a simple identify message. */ or SCB_LUN, MSG_IDENTIFYFLAG call target_outb; /* * If we are the result of a tagged command, send * a simple Q tag and the tag id. */ test SCB_CONTROL, TAG_ENB jz . + 3; mvi MSG_SIMPLE_Q_TAG call target_outb; mov SCB_TARGET_ITAG call target_outb; target_synccmd: /* * Now determine what phases the host wants us * to go through. */ mov SEQ_FLAGS, SCB_TARGET_PHASES; test SCB_CONTROL, MK_MESSAGE jz target_ITloop; mvi P_MESGIN|BSYO call change_phase; jmp host_target_message_loop; target_ITloop: /* * Start honoring ATN signals now that * we properly identified ourselves. */ test SCSISIGI, ATNI jnz target_mesgout; test SEQ_FLAGS, CMDPHASE_PENDING jnz target_cmdphase; test SEQ_FLAGS, DPHASE_PENDING jnz target_dphase; test SEQ_FLAGS, SPHASE_PENDING jnz target_sphase; /* * No more work to do. Either disconnect or not depending * on the state of NO_DISCONNECT. */ test SEQ_FLAGS, NO_DISCONNECT jz target_disconnect; mvi TARG_IMMEDIATE_SCB, SCB_LIST_NULL; call complete_target_cmd; if ((ahc->flags & AHC_PAGESCBS) != 0) { mov ALLZEROS call get_free_or_disc_scb; } cmp TARG_IMMEDIATE_SCB, SCB_LIST_NULL je .; mvi DMAPARAMS, HDMAEN|DIRECTION|FIFORESET; mov TARG_IMMEDIATE_SCB call dma_scb; call set_transfer_settings; or SXFRCTL0, CLRSTCNT|CLRCHN; jmp target_synccmd; target_mesgout: mvi SCSISIGO, P_MESGOUT|BSYO; target_mesgout_continue: call target_inb; target_mesgout_pending: and SEQ_FLAGS2, ~TARGET_MSG_PENDING; /* Local Processing goes here... */ jmp host_target_message_loop; target_disconnect: mvi P_MESGIN|BSYO call change_phase; test SEQ_FLAGS, DPHASE jz . + 2; mvi MSG_SAVEDATAPOINTER call target_outb; mvi MSG_DISCONNECT call target_outb; target_busfree_wait: /* Wait for preceding I/O session to complete. */ test SCSISIGI, ACKI jnz .; target_busfree: and SIMODE1, ~ENBUSFREE; if ((ahc->features & AHC_ULTRA2) != 0) { clr SCSIBUSL; } clr SCSISIGO; mvi LASTPHASE, P_BUSFREE; call complete_target_cmd; jmp poll_for_work; target_cmdphase: /* * The target has dropped ATN (doesn't want to abort or BDR) * and we believe this selection to be valid. If the ring * buffer for new commands is full, return busy or queue full. */ if ((ahc->features & AHC_HS_MAILBOX) != 0) { and A, HOST_TQINPOS, HS_MAILBOX; } else { mov A, KERNEL_TQINPOS; } cmp TQINPOS, A jne tqinfifo_has_space; mvi P_STATUS|BSYO call change_phase; test SEQ_FLAGS, TARGET_CMD_IS_TAGGED jz . + 3; mvi STATUS_QUEUE_FULL call target_outb; jmp target_busfree_wait; mvi STATUS_BUSY call target_outb; jmp target_busfree_wait; tqinfifo_has_space: mvi P_COMMAND|BSYO call change_phase; call target_inb; mov A, DINDEX; /* Store for host */ if ((ahc->features & AHC_CMD_CHAN) != 0) { mov CCSCBRAM, A; } else { mov DFDAT, A; } /* * Determine the number of bytes to read * based on the command group code via table lookup. * We reuse the first 8 bytes of the TARG_SCSIRATE * BIOS array for this table. Count is one less than * the total for the command since we've already fetched * the first byte. */ shr A, CMD_GROUP_CODE_SHIFT; add SINDEX, CMDSIZE_TABLE, A; mov A, SINDIR; test A, 0xFF jz command_phase_done; or SXFRCTL0, SPIOEN; command_loop: test SSTAT0, SPIORDY jz .; cmp A, 1 jne . + 2; and SXFRCTL0, ~SPIOEN; /* Last Byte */ if ((ahc->features & AHC_CMD_CHAN) != 0) { mov CCSCBRAM, SCSIDATL; } else { mov DFDAT, SCSIDATL; } dec A; test A, 0xFF jnz command_loop; command_phase_done: and SEQ_FLAGS, ~CMDPHASE_PENDING; jmp target_ITloop; target_dphase: /* * Data phases on the bus are from the * perspective of the initiator. The DMA * code looks at LASTPHASE to determine the * data direction of the DMA. Toggle it for * target transfers. */ xor LASTPHASE, IOI, SCB_TARGET_DATA_DIR; or SCB_TARGET_DATA_DIR, BSYO call change_phase; jmp p_data; target_sphase: mvi P_STATUS|BSYO call change_phase; mvi LASTPHASE, P_STATUS; mov SCB_SCSI_STATUS call target_outb; /* XXX Watch for ATN or parity errors??? */ mvi SCSISIGO, P_MESGIN|BSYO; /* MSG_CMDCMPLT is 0, but we can't do an immediate of 0 */ mov ALLZEROS call target_outb; jmp target_busfree_wait; complete_target_cmd: test SEQ_FLAGS, TARG_CMD_PENDING jnz . + 2; mov SCB_TAG jmp complete_post; if ((ahc->features & AHC_CMD_CHAN) != 0) { /* Set the valid byte */ mvi CCSCBADDR, 24; mov CCSCBRAM, ALLONES; mvi CCHCNT, 28; or CCSCBCTL, CCSCBEN|CCSCBRESET; test CCSCBCTL, CCSCBDONE jz .; clr CCSCBCTL; } else { /* Set the valid byte */ or DFCNTRL, FIFORESET; mvi DFWADDR, 3; /* Third 64bit word or byte 24 */ mov DFDAT, ALLONES; mvi 28 call set_hcnt; or DFCNTRL, HDMAEN|FIFOFLUSH; call dma_finish; } inc TQINPOS; mvi INTSTAT,CMDCMPLT ret; } if ((ahc->flags & AHC_INITIATORROLE) != 0) { initiator_select: or SXFRCTL0, SPIOEN|CLRSTCNT|CLRCHN; /* * As soon as we get a successful selection, the target * should go into the message out phase since we have ATN * asserted. */ mvi MSG_OUT, MSG_IDENTIFYFLAG; mvi SEQ_FLAGS, NO_CDB_SENT; mvi CLRSINT0, CLRSELDO; /* * Main loop for information transfer phases. Wait for the * target to assert REQ before checking MSG, C/D and I/O for * the bus phase. */ mesgin_phasemis: ITloop: call phase_lock; mov A, LASTPHASE; test A, ~P_DATAIN jz p_data; cmp A,P_COMMAND je p_command; cmp A,P_MESGOUT je p_mesgout; cmp A,P_STATUS je p_status; cmp A,P_MESGIN je p_mesgin; mvi BAD_PHASE call set_seqint; jmp ITloop; /* Try reading the bus again. */ await_busfree: and SIMODE1, ~ENBUSFREE; mov NONE, SCSIDATL; /* Ack the last byte */ if ((ahc->features & AHC_ULTRA2) != 0) { clr SCSIBUSL; /* Prevent bit leakage durint SELTO */ } and SXFRCTL0, ~SPIOEN; test SSTAT1,REQINIT|BUSFREE jz .; test SSTAT1, BUSFREE jnz poll_for_work; mvi MISSED_BUSFREE call set_seqint; } clear_target_state: /* * We assume that the kernel driver may reset us * at any time, even in the middle of a DMA, so * clear DFCNTRL too. */ clr DFCNTRL; or SXFRCTL0, CLRSTCNT|CLRCHN; /* * We don't know the target we will connect to, * so default to narrow transfers to avoid * parity problems. */ if ((ahc->features & AHC_ULTRA2) != 0) { bmov SCSIRATE, ALLZEROS, 2; } else { clr SCSIRATE; if ((ahc->features & AHC_ULTRA) != 0) { and SXFRCTL0, ~(FAST20); } } mvi LASTPHASE, P_BUSFREE; /* clear target specific flags */ mvi SEQ_FLAGS, NOT_IDENTIFIED|NO_CDB_SENT ret; sg_advance: clr A; /* add sizeof(struct scatter) */ add SCB_RESIDUAL_SGPTR[0],SG_SIZEOF; adc SCB_RESIDUAL_SGPTR[1],A; adc SCB_RESIDUAL_SGPTR[2],A; adc SCB_RESIDUAL_SGPTR[3],A ret; if ((ahc->features & AHC_CMD_CHAN) != 0) { disable_ccsgen: test CCSGCTL, CCSGEN jz return; test CCSGCTL, CCSGDONE jz .; disable_ccsgen_fetch_done: clr CCSGCTL; test CCSGCTL, CCSGEN jnz .; ret; idle_loop: /* * Do we need any more segments for this transfer? */ test SCB_RESIDUAL_DATACNT[3], SG_LAST_SEG jnz return; /* Did we just finish fetching segs? */ cmp CCSGCTL, CCSGEN|CCSGDONE je idle_sgfetch_complete; /* Are we actively fetching segments? */ test CCSGCTL, CCSGEN jnz return; /* * Do we have any prefetch left??? */ cmp CCSGADDR, SG_PREFETCH_CNT jne idle_sg_avail; /* * Need to fetch segments, but we can only do that * if the command channel is completely idle. Make * sure we don't have an SCB prefetch going on. */ test CCSCBCTL, CCSCBEN jnz return; /* * We fetch a "cacheline aligned" and sized amount of data * so we don't end up referencing a non-existent page. * Cacheline aligned is in quotes because the kernel will * set the prefetch amount to a reasonable level if the * cacheline size is unknown. */ mvi CCHCNT, SG_PREFETCH_CNT; and CCHADDR[0], SG_PREFETCH_ALIGN_MASK, SCB_RESIDUAL_SGPTR; bmov CCHADDR[1], SCB_RESIDUAL_SGPTR[1], 3; mvi CCSGCTL, CCSGEN|CCSGRESET ret; idle_sgfetch_complete: call disable_ccsgen_fetch_done; and CCSGADDR, SG_PREFETCH_ADDR_MASK, SCB_RESIDUAL_SGPTR; idle_sg_avail: if ((ahc->features & AHC_ULTRA2) != 0) { /* Does the hardware have space for another SG entry? */ test DFSTATUS, PRELOAD_AVAIL jz return; bmov HADDR, CCSGRAM, 7; test HCNT[0], 0x1 jz . + 2; xor DATA_COUNT_ODD, 0x1; bmov SCB_RESIDUAL_DATACNT[3], CCSGRAM, 1; if ((ahc->flags & AHC_39BIT_ADDRESSING) != 0) { mov SCB_RESIDUAL_DATACNT[3] call set_hhaddr; } call sg_advance; mov SINDEX, SCB_RESIDUAL_SGPTR[0]; test DATA_COUNT_ODD, 0x1 jz . + 2; or SINDEX, ODD_SEG; test SCB_RESIDUAL_DATACNT[3], SG_LAST_SEG jz . + 2; or SINDEX, LAST_SEG; mov SG_CACHE_PRE, SINDEX; /* Load the segment */ or DFCNTRL, PRELOADEN; } ret; } if ((ahc->bugs & AHC_PCI_MWI_BUG) != 0 && ahc->pci_cachesize != 0) { /* * Calculate the trailing portion of this S/G segment that cannot * be transferred using memory write and invalidate PCI transactions. * XXX Can we optimize this for PCI writes only??? */ calc_mwi_residual: /* * If the ending address is on a cacheline boundary, * there is no need for an extra segment. */ mov A, HCNT[0]; add A, A, HADDR[0]; and A, CACHESIZE_MASK; test A, 0xFF jz return; /* * If the transfer is less than a cachline, * there is no need for an extra segment. */ test HCNT[1], 0xFF jnz calc_mwi_residual_final; test HCNT[2], 0xFF jnz calc_mwi_residual_final; add NONE, INVERTED_CACHESIZE_MASK, HCNT[0]; jnc return; calc_mwi_residual_final: mov MWI_RESIDUAL, A; not A; inc A; add HCNT[0], A; adc HCNT[1], -1; adc HCNT[2], -1 ret; } p_data: test SEQ_FLAGS,NOT_IDENTIFIED|NO_CDB_SENT jz p_data_allowed; mvi PROTO_VIOLATION call set_seqint; p_data_allowed: if ((ahc->features & AHC_ULTRA2) != 0) { mvi DMAPARAMS, PRELOADEN|SCSIEN|HDMAEN; } else { mvi DMAPARAMS, WIDEODD|SCSIEN|SDMAEN|HDMAEN|FIFORESET; } test LASTPHASE, IOI jnz . + 2; or DMAPARAMS, DIRECTION; if ((ahc->features & AHC_CMD_CHAN) != 0) { /* We don't have any valid S/G elements */ mvi CCSGADDR, SG_PREFETCH_CNT; } test SEQ_FLAGS, DPHASE jz data_phase_initialize; /* * If we re-enter the data phase after going through another * phase, our transfer location has almost certainly been * corrupted by the interveining, non-data, transfers. Ask * the host driver to fix us up based on the transfer residual. */ mvi PDATA_REINIT call set_seqint; jmp data_phase_loop; data_phase_initialize: /* We have seen a data phase for the first time */ or SEQ_FLAGS, DPHASE; /* * Initialize the DMA address and counter from the SCB. * Also set SCB_RESIDUAL_SGPTR, including the LAST_SEG * flag in the highest byte of the data count. We cannot * modify the saved values in the SCB until we see a save * data pointers message. */ if ((ahc->flags & AHC_39BIT_ADDRESSING) != 0) { /* The lowest address byte must be loaded last. */ mov SCB_DATACNT[3] call set_hhaddr; } if ((ahc->features & AHC_CMD_CHAN) != 0) { bmov HADDR, SCB_DATAPTR, 7; bmov SCB_RESIDUAL_DATACNT[3], SCB_DATACNT[3], 5; } else { mvi DINDEX, HADDR; mvi SCB_DATAPTR call bcopy_7; mvi DINDEX, SCB_RESIDUAL_DATACNT + 3; mvi SCB_DATACNT + 3 call bcopy_5; } if ((ahc->bugs & AHC_PCI_MWI_BUG) != 0 && ahc->pci_cachesize != 0) { call calc_mwi_residual; } and SCB_RESIDUAL_SGPTR[0], ~SG_FULL_RESID; and DATA_COUNT_ODD, 0x1, HCNT[0]; if ((ahc->features & AHC_ULTRA2) == 0) { if ((ahc->features & AHC_CMD_CHAN) != 0) { bmov STCNT, HCNT, 3; } else { call set_stcnt_from_hcnt; } } data_phase_loop: /* Guard against overruns */ test SCB_RESIDUAL_SGPTR[0], SG_LIST_NULL jz data_phase_inbounds; /* * Turn on `Bit Bucket' mode, wait until the target takes * us to another phase, and then notify the host. */ and DMAPARAMS, DIRECTION; mov DFCNTRL, DMAPARAMS; or SXFRCTL1,BITBUCKET; if ((ahc->features & AHC_DT) == 0) { test SSTAT1,PHASEMIS jz .; } else { test SCSIPHASE, DATA_PHASE_MASK jnz .; } and SXFRCTL1, ~BITBUCKET; mvi DATA_OVERRUN call set_seqint; jmp ITloop; data_phase_inbounds: if ((ahc->features & AHC_ULTRA2) != 0) { mov SINDEX, SCB_RESIDUAL_SGPTR[0]; test SCB_RESIDUAL_DATACNT[3], SG_LAST_SEG jz . + 2; or SINDEX, LAST_SEG; test DATA_COUNT_ODD, 0x1 jz . + 2; or SINDEX, ODD_SEG; mov SG_CACHE_PRE, SINDEX; mov DFCNTRL, DMAPARAMS; ultra2_dma_loop: call idle_loop; /* * The transfer is complete if either the last segment * completes or the target changes phase. */ test SG_CACHE_SHADOW, LAST_SEG_DONE jnz ultra2_dmafinish; if ((ahc->features & AHC_DT) == 0) { if ((ahc->flags & AHC_TARGETROLE) != 0) { /* * As a target, we control the phases, * so ignore PHASEMIS. */ test SSTAT0, TARGET jnz ultra2_dma_loop; } if ((ahc->flags & AHC_INITIATORROLE) != 0) { test SSTAT1,PHASEMIS jz ultra2_dma_loop; } } else { test DFCNTRL, SCSIEN jnz ultra2_dma_loop; } ultra2_dmafinish: /* * The transfer has terminated either due to a phase * change, and/or the completion of the last segment. * We have two goals here. Do as much other work * as possible while the data fifo drains on a read * and respond as quickly as possible to the standard * messages (save data pointers/disconnect and command * complete) that usually follow a data phase. */ if ((ahc->bugs & AHC_AUTOFLUSH_BUG) != 0) { /* * On chips with broken auto-flush, start * the flushing process now. We'll poke * the chip from time to time to keep the * flush process going as we complete the * data phase. */ or DFCNTRL, FIFOFLUSH; } /* * We assume that, even though data may still be * transferring to the host, that the SCSI side of * the DMA engine is now in a static state. This * allows us to update our notion of where we are * in this transfer. * * If, by chance, we stopped before being able * to fetch additional segments for this transfer, * yet the last S/G was completely exhausted, * call our idle loop until it is able to load * another segment. This will allow us to immediately * pickup on the next segment on the next data phase. * * If we happened to stop on the last segment, then * our residual information is still correct from * the idle loop and there is no need to perform * any fixups. */ ultra2_ensure_sg: test SG_CACHE_SHADOW, LAST_SEG jz ultra2_shvalid; /* Record if we've consumed all S/G entries */ test SSTAT2, SHVALID jnz residuals_correct; or SCB_RESIDUAL_SGPTR[0], SG_LIST_NULL; jmp residuals_correct; ultra2_shvalid: test SSTAT2, SHVALID jnz sgptr_fixup; call idle_loop; jmp ultra2_ensure_sg; sgptr_fixup: /* * Fixup the residual next S/G pointer. The S/G preload * feature of the chip allows us to load two elements * in addition to the currently active element. We * store the bottom byte of the next S/G pointer in * the SG_CACEPTR register so we can restore the * correct value when the DMA completes. If the next * sg ptr value has advanced to the point where higher * bytes in the address have been affected, fix them * too. */ test SG_CACHE_SHADOW, 0x80 jz sgptr_fixup_done; test SCB_RESIDUAL_SGPTR[0], 0x80 jnz sgptr_fixup_done; add SCB_RESIDUAL_SGPTR[1], -1; adc SCB_RESIDUAL_SGPTR[2], -1; adc SCB_RESIDUAL_SGPTR[3], -1; sgptr_fixup_done: and SCB_RESIDUAL_SGPTR[0], SG_ADDR_MASK, SG_CACHE_SHADOW; clr DATA_COUNT_ODD; test SG_CACHE_SHADOW, ODD_SEG jz . + 2; or DATA_COUNT_ODD, 0x1; clr SCB_RESIDUAL_DATACNT[3]; /* We are not the last seg */ residuals_correct: /* * Go ahead and shut down the DMA engine now. * In the future, we'll want to handle end of * transfer messages prior to doing this, but this * requires similar restructuring for pre-ULTRA2 * controllers. */ test DMAPARAMS, DIRECTION jnz ultra2_fifoempty; ultra2_fifoflush: if ((ahc->features & AHC_DT) == 0) { if ((ahc->bugs & AHC_AUTOFLUSH_BUG) != 0) { /* * On Rev A of the aic7890, the autoflush * feature doesn't function correctly. * Perform an explicit manual flush. During * a manual flush, the FIFOEMP bit becomes * true every time the PCI FIFO empties * regardless of the state of the SCSI FIFO. * It can take up to 4 clock cycles for the * SCSI FIFO to get data into the PCI FIFO * and for FIFOEMP to de-assert. Here we * guard against this condition by making * sure the FIFOEMP bit stays on for 5 full * clock cycles. */ or DFCNTRL, FIFOFLUSH; test DFSTATUS, FIFOEMP jz ultra2_fifoflush; test DFSTATUS, FIFOEMP jz ultra2_fifoflush; test DFSTATUS, FIFOEMP jz ultra2_fifoflush; test DFSTATUS, FIFOEMP jz ultra2_fifoflush; } test DFSTATUS, FIFOEMP jz ultra2_fifoflush; } else { /* * We enable the auto-ack feature on DT capable * controllers. This means that the controller may * have already transferred some overrun bytes into * the data FIFO and acked them on the bus. The only * way to detect this situation is to wait for * LAST_SEG_DONE to come true on a completed transfer * and then test to see if the data FIFO is non-empty. */ test SCB_RESIDUAL_SGPTR[0], SG_LIST_NULL jz . + 4; test SG_CACHE_SHADOW, LAST_SEG_DONE jz .; test DFSTATUS, FIFOEMP jnz ultra2_fifoempty; /* Overrun */ jmp data_phase_loop; test DFSTATUS, FIFOEMP jz .; } ultra2_fifoempty: /* Don't clobber an inprogress host data transfer */ test DFSTATUS, MREQPEND jnz ultra2_fifoempty; ultra2_dmahalt: and DFCNTRL, ~(SCSIEN|HDMAEN); test DFCNTRL, SCSIEN|HDMAEN jnz .; if ((ahc->flags & AHC_39BIT_ADDRESSING) != 0) { /* * Keep HHADDR cleared for future, 32bit addressed * only, DMA operations. * * Due to bayonette style S/G handling, our residual * data must be "fixed up" once the transfer is halted. * Here we fixup the HSHADDR stored in the high byte * of the residual data cnt. By postponing the fixup, * we can batch the clearing of HADDR with the fixup. * If we halted on the last segment, the residual is * already correct. If we are not on the last * segment, copy the high address directly from HSHADDR. * We don't need to worry about maintaining the * SG_LAST_SEG flag as it will always be false in the * case where an update is required. */ or DSCOMMAND1, HADDLDSEL0; test SG_CACHE_SHADOW, LAST_SEG jnz . + 2; mov SCB_RESIDUAL_DATACNT[3], SHADDR; clr HADDR; and DSCOMMAND1, ~HADDLDSEL0; } } else { /* If we are the last SG block, tell the hardware. */ if ((ahc->bugs & AHC_PCI_MWI_BUG) != 0 && ahc->pci_cachesize != 0) { test MWI_RESIDUAL, 0xFF jnz dma_mid_sg; } test SCB_RESIDUAL_DATACNT[3], SG_LAST_SEG jz dma_mid_sg; if ((ahc->flags & AHC_TARGETROLE) != 0) { test SSTAT0, TARGET jz dma_last_sg; if ((ahc->flags & AHC_TMODE_WIDEODD_BUG) != 0) { test DMAPARAMS, DIRECTION jz dma_mid_sg; } } dma_last_sg: and DMAPARAMS, ~WIDEODD; dma_mid_sg: /* Start DMA data transfer. */ mov DFCNTRL, DMAPARAMS; dma_loop: if ((ahc->features & AHC_CMD_CHAN) != 0) { call idle_loop; } test SSTAT0,DMADONE jnz dma_dmadone; test SSTAT1,PHASEMIS jz dma_loop; /* ie. underrun */ dma_phasemis: /* * We will be "done" DMAing when the transfer count goes to * zero, or the target changes the phase (in light of this, * it makes sense that the DMA circuitry doesn't ACK when * PHASEMIS is active). If we are doing a SCSI->Host transfer, * the data FIFO should be flushed auto-magically on STCNT=0 * or a phase change, so just wait for FIFO empty status. */ dma_checkfifo: test DFCNTRL,DIRECTION jnz dma_fifoempty; dma_fifoflush: test DFSTATUS,FIFOEMP jz dma_fifoflush; dma_fifoempty: /* Don't clobber an inprogress host data transfer */ test DFSTATUS, MREQPEND jnz dma_fifoempty; /* * Now shut off the DMA and make sure that the DMA * hardware has actually stopped. Touching the DMA * counters, etc. while a DMA is active will result * in an ILLSADDR exception. */ dma_dmadone: and DFCNTRL, ~(SCSIEN|SDMAEN|HDMAEN); dma_halt: /* * Some revisions of the aic78XX have a problem where, if the * data fifo is full, but the PCI input latch is not empty, * HDMAEN cannot be cleared. The fix used here is to drain * the prefetched but unused data from the data fifo until * there is space for the input latch to drain. */ if ((ahc->bugs & AHC_PCI_2_1_RETRY_BUG) != 0) { mov NONE, DFDAT; } test DFCNTRL, (SCSIEN|SDMAEN|HDMAEN) jnz dma_halt; /* See if we have completed this last segment */ test STCNT[0], 0xff jnz data_phase_finish; test STCNT[1], 0xff jnz data_phase_finish; test STCNT[2], 0xff jnz data_phase_finish; /* * Advance the scatter-gather pointers if needed */ if ((ahc->bugs & AHC_PCI_MWI_BUG) != 0 && ahc->pci_cachesize != 0) { test MWI_RESIDUAL, 0xFF jz no_mwi_resid; /* * Reload HADDR from SHADDR and setup the * count to be the size of our residual. */ if ((ahc->features & AHC_CMD_CHAN) != 0) { bmov HADDR, SHADDR, 4; mov HCNT, MWI_RESIDUAL; bmov HCNT[1], ALLZEROS, 2; } else { mvi DINDEX, HADDR; mvi SHADDR call bcopy_4; mov MWI_RESIDUAL call set_hcnt; } clr MWI_RESIDUAL; jmp sg_load_done; no_mwi_resid: } test SCB_RESIDUAL_DATACNT[3], SG_LAST_SEG jz sg_load; or SCB_RESIDUAL_SGPTR[0], SG_LIST_NULL; jmp data_phase_finish; sg_load: /* * Load the next SG element's data address and length * into the DMA engine. If we don't have hardware * to perform a prefetch, we'll have to fetch the * segment from host memory first. */ if ((ahc->features & AHC_CMD_CHAN) != 0) { /* Wait for the idle loop to complete */ test CCSGCTL, CCSGEN jz . + 3; call idle_loop; test CCSGCTL, CCSGEN jnz . - 1; bmov HADDR, CCSGRAM, 7; /* * Workaround for flaky external SCB RAM * on certain aic7895 setups. It seems * unable to handle direct transfers from * S/G ram to certain SCB locations. */ mov SINDEX, CCSGRAM; mov SCB_RESIDUAL_DATACNT[3], SINDEX; } else { if ((ahc->flags & AHC_39BIT_ADDRESSING) != 0) { mov ALLZEROS call set_hhaddr; } mvi DINDEX, HADDR; mvi SCB_RESIDUAL_SGPTR call bcopy_4; mvi SG_SIZEOF call set_hcnt; or DFCNTRL, HDMAEN|DIRECTION|FIFORESET; call dma_finish; mvi DINDEX, HADDR; call dfdat_in_7; mov SCB_RESIDUAL_DATACNT[3], DFDAT; } if ((ahc->flags & AHC_39BIT_ADDRESSING) != 0) { mov SCB_RESIDUAL_DATACNT[3] call set_hhaddr; /* * The lowest address byte must be loaded * last as it triggers the computation of * some items in the PCI block. The ULTRA2 * chips do this on PRELOAD. */ mov HADDR, HADDR; } if ((ahc->bugs & AHC_PCI_MWI_BUG) != 0 && ahc->pci_cachesize != 0) { call calc_mwi_residual; } /* Point to the new next sg in memory */ call sg_advance; sg_load_done: if ((ahc->features & AHC_CMD_CHAN) != 0) { bmov STCNT, HCNT, 3; } else { call set_stcnt_from_hcnt; } /* Track odd'ness */ test HCNT[0], 0x1 jz . + 2; xor DATA_COUNT_ODD, 0x1; if ((ahc->flags & AHC_TARGETROLE) != 0) { test SSTAT0, TARGET jnz data_phase_loop; } } data_phase_finish: /* * If the target has left us in data phase, loop through * the DMA code again. In the case of ULTRA2 adapters, * we should only loop if there is a data overrun. For * all other adapters, we'll loop after each S/G element * is loaded as well as if there is an overrun. */ if ((ahc->flags & AHC_TARGETROLE) != 0) { test SSTAT0, TARGET jnz data_phase_done; } if ((ahc->flags & AHC_INITIATORROLE) != 0) { test SSTAT1, REQINIT jz .; if ((ahc->features & AHC_DT) == 0) { test SSTAT1,PHASEMIS jz data_phase_loop; } else { test SCSIPHASE, DATA_PHASE_MASK jnz data_phase_loop; } } data_phase_done: /* * After a DMA finishes, save the SG and STCNT residuals back into * the SCB. We use STCNT instead of HCNT, since it's a reflection * of how many bytes were transferred on the SCSI (as opposed to the * host) bus. */ if ((ahc->features & AHC_CMD_CHAN) != 0) { /* Kill off any pending prefetch */ call disable_ccsgen; } if ((ahc->features & AHC_ULTRA2) == 0) { /* * Clear the high address byte so that all other DMA * operations, which use 32bit addressing, can assume * HHADDR is 0. */ if ((ahc->flags & AHC_39BIT_ADDRESSING) != 0) { mov ALLZEROS call set_hhaddr; } } /* * Update our residual information before the information is * lost by some other type of SCSI I/O (e.g. PIO). If we have * transferred all data, no update is needed. * */ test SCB_RESIDUAL_SGPTR, SG_LIST_NULL jnz residual_update_done; if ((ahc->bugs & AHC_PCI_MWI_BUG) != 0 && ahc->pci_cachesize != 0) { if ((ahc->features & AHC_CMD_CHAN) != 0) { test MWI_RESIDUAL, 0xFF jz bmov_resid; } mov A, MWI_RESIDUAL; add SCB_RESIDUAL_DATACNT[0], A, STCNT[0]; clr A; adc SCB_RESIDUAL_DATACNT[1], A, STCNT[1]; adc SCB_RESIDUAL_DATACNT[2], A, STCNT[2]; clr MWI_RESIDUAL; if ((ahc->features & AHC_CMD_CHAN) != 0) { jmp . + 2; bmov_resid: bmov SCB_RESIDUAL_DATACNT, STCNT, 3; } } else if ((ahc->features & AHC_CMD_CHAN) != 0) { bmov SCB_RESIDUAL_DATACNT, STCNT, 3; } else { mov SCB_RESIDUAL_DATACNT[0], STCNT[0]; mov SCB_RESIDUAL_DATACNT[1], STCNT[1]; mov SCB_RESIDUAL_DATACNT[2], STCNT[2]; } residual_update_done: /* * Since we've been through a data phase, the SCB_RESID* fields * are now initialized. Clear the full residual flag. */ and SCB_SGPTR[0], ~SG_FULL_RESID; if ((ahc->features & AHC_ULTRA2) != 0) { /* Clear the channel in case we return to data phase later */ or SXFRCTL0, CLRSTCNT|CLRCHN; or SXFRCTL0, CLRSTCNT|CLRCHN; } if ((ahc->flags & AHC_TARGETROLE) != 0) { test SEQ_FLAGS, DPHASE_PENDING jz ITloop; and SEQ_FLAGS, ~DPHASE_PENDING; /* * For data-in phases, wait for any pending acks from the * initiator before changing phase. We only need to * send Ignore Wide Residue messages for data-in phases. */ test DFCNTRL, DIRECTION jz target_ITloop; test SSTAT1, REQINIT jnz .; test DATA_COUNT_ODD, 0x1 jz target_ITloop; test SCSIRATE, WIDEXFER jz target_ITloop; /* * Issue an Ignore Wide Residue Message. */ mvi P_MESGIN|BSYO call change_phase; mvi MSG_IGN_WIDE_RESIDUE call target_outb; mvi 1 call target_outb; jmp target_ITloop; } else { jmp ITloop; } if ((ahc->flags & AHC_INITIATORROLE) != 0) { /* * Command phase. Set up the DMA registers and let 'er rip. */ p_command: test SEQ_FLAGS, NOT_IDENTIFIED jz p_command_okay; mvi PROTO_VIOLATION call set_seqint; p_command_okay: if ((ahc->features & AHC_ULTRA2) != 0) { bmov HCNT[0], SCB_CDB_LEN, 1; bmov HCNT[1], ALLZEROS, 2; mvi SG_CACHE_PRE, LAST_SEG; } else if ((ahc->features & AHC_CMD_CHAN) != 0) { bmov STCNT[0], SCB_CDB_LEN, 1; bmov STCNT[1], ALLZEROS, 2; } else { mov STCNT[0], SCB_CDB_LEN; clr STCNT[1]; clr STCNT[2]; } add NONE, -13, SCB_CDB_LEN; mvi SCB_CDB_STORE jnc p_command_embedded; p_command_from_host: if ((ahc->features & AHC_ULTRA2) != 0) { bmov HADDR[0], SCB_CDB_PTR, 4; mvi DFCNTRL, (PRELOADEN|SCSIEN|HDMAEN|DIRECTION); } else { if ((ahc->features & AHC_CMD_CHAN) != 0) { bmov HADDR[0], SCB_CDB_PTR, 4; bmov HCNT, STCNT, 3; } else { mvi DINDEX, HADDR; mvi SCB_CDB_PTR call bcopy_4; mov SCB_CDB_LEN call set_hcnt; } mvi DFCNTRL, (SCSIEN|SDMAEN|HDMAEN|DIRECTION|FIFORESET); } jmp p_command_xfer; p_command_embedded: /* * The data fifo seems to require 4 byte aligned * transfers from the sequencer. Force this to * be the case by clearing HADDR[0] even though * we aren't going to touch host memory. */ clr HADDR[0]; if ((ahc->features & AHC_ULTRA2) != 0) { mvi DFCNTRL, (PRELOADEN|SCSIEN|DIRECTION); bmov DFDAT, SCB_CDB_STORE, 12; } else if ((ahc->features & AHC_CMD_CHAN) != 0) { if ((ahc->flags & AHC_SCB_BTT) != 0) { /* * On the 7895 the data FIFO will * get corrupted if you try to dump * data from external SCB memory into * the FIFO while it is enabled. So, * fill the fifo and then enable SCSI * transfers. */ mvi DFCNTRL, (DIRECTION|FIFORESET); } else { mvi DFCNTRL, (SCSIEN|SDMAEN|DIRECTION|FIFORESET); } bmov DFDAT, SCB_CDB_STORE, 12; if ((ahc->flags & AHC_SCB_BTT) != 0) { mvi DFCNTRL, (SCSIEN|SDMAEN|DIRECTION|FIFOFLUSH); } else { or DFCNTRL, FIFOFLUSH; } } else { mvi DFCNTRL, (SCSIEN|SDMAEN|DIRECTION|FIFORESET); call copy_to_fifo_6; call copy_to_fifo_6; or DFCNTRL, FIFOFLUSH; } p_command_xfer: and SEQ_FLAGS, ~NO_CDB_SENT; if ((ahc->features & AHC_DT) == 0) { test SSTAT0, SDONE jnz . + 2; test SSTAT1, PHASEMIS jz . - 1; /* * Wait for our ACK to go-away on its own * instead of being killed by SCSIEN getting cleared. */ test SCSISIGI, ACKI jnz .; } else { test DFCNTRL, SCSIEN jnz .; } test SSTAT0, SDONE jnz p_command_successful; /* * Don't allow a data phase if the command * was not fully transferred. */ or SEQ_FLAGS, NO_CDB_SENT; p_command_successful: and DFCNTRL, ~(SCSIEN|SDMAEN|HDMAEN); test DFCNTRL, (SCSIEN|SDMAEN|HDMAEN) jnz .; jmp ITloop; /* * Status phase. Wait for the data byte to appear, then read it * and store it into the SCB. */ p_status: test SEQ_FLAGS, NOT_IDENTIFIED jnz mesgin_proto_violation; p_status_okay: mov SCB_SCSI_STATUS, SCSIDATL; or SCB_CONTROL, STATUS_RCVD; jmp ITloop; /* * Message out phase. If MSG_OUT is MSG_IDENTIFYFLAG, build a full * identify message sequence and send it to the target. The host may * override this behavior by setting the MK_MESSAGE bit in the SCB * control byte. This will cause us to interrupt the host and allow * it to handle the message phase completely on its own. If the bit * associated with this target is set, we will also interrupt the host, * thereby allowing it to send a message on the next selection regardless * of the transaction being sent. * * If MSG_OUT is == HOST_MSG, also interrupt the host and take a message. * This is done to allow the host to send messages outside of an identify * sequence while protecting the sequencer from testing the MK_MESSAGE bit * on an SCB that might not be for the current nexus. (For example, a * BDR message in response to a bad reselection would leave us pointed to * an SCB that doesn't have anything to do with the current target). * * Otherwise, treat MSG_OUT as a 1 byte message to send (abort, abort tag, * bus device reset). * * When there are no messages to send, MSG_OUT should be set to MSG_NOOP, * in case the target decides to put us in this phase for some strange * reason. */ p_mesgout_retry: /* Turn on ATN for the retry */ if ((ahc->features & AHC_DT) == 0) { or SCSISIGO, ATNO, LASTPHASE; } else { mvi SCSISIGO, ATNO; } p_mesgout: mov SINDEX, MSG_OUT; cmp SINDEX, MSG_IDENTIFYFLAG jne p_mesgout_from_host; test SCB_CONTROL,MK_MESSAGE jnz host_message_loop; p_mesgout_identify: or SINDEX, MSG_IDENTIFYFLAG|DISCENB, SCB_LUN; test SCB_CONTROL, DISCENB jnz . + 2; and SINDEX, ~DISCENB; /* * Send a tag message if TAG_ENB is set in the SCB control block. * Use SCB_TAG (the position in the kernel's SCB array) as the tag value. */ p_mesgout_tag: test SCB_CONTROL,TAG_ENB jz p_mesgout_onebyte; mov SCSIDATL, SINDEX; /* Send the identify message */ call phase_lock; cmp LASTPHASE, P_MESGOUT jne p_mesgout_done; and SCSIDATL,TAG_ENB|SCB_TAG_TYPE,SCB_CONTROL; call phase_lock; cmp LASTPHASE, P_MESGOUT jne p_mesgout_done; mov SCB_TAG jmp p_mesgout_onebyte; /* * Interrupt the driver, and allow it to handle this message * phase and any required retries. */ p_mesgout_from_host: cmp SINDEX, HOST_MSG jne p_mesgout_onebyte; jmp host_message_loop; p_mesgout_onebyte: mvi CLRSINT1, CLRATNO; mov SCSIDATL, SINDEX; /* * If the next bus phase after ATN drops is message out, it means * that the target is requesting that the last message(s) be resent. */ call phase_lock; cmp LASTPHASE, P_MESGOUT je p_mesgout_retry; p_mesgout_done: mvi CLRSINT1,CLRATNO; /* Be sure to turn ATNO off */ mov LAST_MSG, MSG_OUT; mvi MSG_OUT, MSG_NOOP; /* No message left */ jmp ITloop; /* * Message in phase. Bytes are read using Automatic PIO mode. */ p_mesgin: mvi ACCUM call inb_first; /* read the 1st message byte */ test A,MSG_IDENTIFYFLAG jnz mesgin_identify; cmp A,MSG_DISCONNECT je mesgin_disconnect; cmp A,MSG_SAVEDATAPOINTER je mesgin_sdptrs; cmp ALLZEROS,A je mesgin_complete; cmp A,MSG_RESTOREPOINTERS je mesgin_rdptrs; cmp A,MSG_IGN_WIDE_RESIDUE je mesgin_ign_wide_residue; cmp A,MSG_NOOP je mesgin_done; /* * Pushed message loop to allow the kernel to * run its own message state engine. To avoid an * extra nop instruction after signaling the kernel, * we perform the phase_lock before checking to see * if we should exit the loop and skip the phase_lock * in the ITloop. Performing back to back phase_locks * shouldn't hurt, but why do it twice... */ host_message_loop: mvi HOST_MSG_LOOP call set_seqint; call phase_lock; cmp RETURN_1, EXIT_MSG_LOOP je ITloop + 1; jmp host_message_loop; mesgin_ign_wide_residue: if ((ahc->features & AHC_WIDE) != 0) { test SCSIRATE, WIDEXFER jz mesgin_reject; /* Pull the residue byte */ mvi ARG_1 call inb_next; cmp ARG_1, 0x01 jne mesgin_reject; test SCB_RESIDUAL_SGPTR[0], SG_LIST_NULL jz . + 2; test DATA_COUNT_ODD, 0x1 jz mesgin_done; mvi IGN_WIDE_RES call set_seqint; jmp mesgin_done; } mesgin_proto_violation: mvi PROTO_VIOLATION call set_seqint; jmp mesgin_done; mesgin_reject: mvi MSG_MESSAGE_REJECT call mk_mesg; mesgin_done: mov NONE,SCSIDATL; /*dummy read from latch to ACK*/ jmp ITloop; /* * We received a "command complete" message. Put the SCB_TAG into the QOUTFIFO, * and trigger a completion interrupt. Before doing so, check to see if there * is a residual or the status byte is something other than STATUS_GOOD (0). * In either of these conditions, we upload the SCB back to the host so it can * process this information. In the case of a non zero status byte, we * additionally interrupt the kernel driver synchronously, allowing it to * decide if sense should be retrieved. If the kernel driver wishes to request * sense, it will fill the kernel SCB with a request sense command, requeue * it to the QINFIFO and tell us not to post to the QOUTFIFO by setting * RETURN_1 to SEND_SENSE. */ mesgin_complete: /* * If ATN is raised, we still want to give the target a message. * Perhaps there was a parity error on this last message byte. * Either way, the target should take us to message out phase * and then attempt to complete the command again. We should use a * critical section here to guard against a timeout triggering * for this command and setting ATN while we are still processing * the completion. test SCSISIGI, ATNI jnz mesgin_done; */ /* * If we are identified and have successfully sent the CDB, * any status will do. Optimize this fast path. */ test SCB_CONTROL, STATUS_RCVD jz mesgin_proto_violation; test SEQ_FLAGS, NOT_IDENTIFIED|NO_CDB_SENT jz complete_accepted; /* * If the target never sent an identify message but instead went * to mesgin to give an invalid message, let the host abort us. */ test SEQ_FLAGS, NOT_IDENTIFIED jnz mesgin_proto_violation; /* * If we received good status but never successfully sent the * cdb, abort the command. */ test SCB_SCSI_STATUS,0xff jnz complete_accepted; test SEQ_FLAGS, NO_CDB_SENT jnz mesgin_proto_violation; complete_accepted: /* * See if we attempted to deliver a message but the target ignored us. */ test SCB_CONTROL, MK_MESSAGE jz . + 2; mvi MKMSG_FAILED call set_seqint; /* * Check for residuals */ test SCB_SGPTR, SG_LIST_NULL jnz check_status;/* No xfer */ test SCB_SGPTR, SG_FULL_RESID jnz upload_scb;/* Never xfered */ test SCB_RESIDUAL_SGPTR, SG_LIST_NULL jz upload_scb; check_status: test SCB_SCSI_STATUS,0xff jz complete; /* Good Status? */ upload_scb: or SCB_SGPTR, SG_RESID_VALID; mvi DMAPARAMS, FIFORESET; mov SCB_TAG call dma_scb; test SCB_SCSI_STATUS, 0xff jz complete; /* Just a residual? */ mvi BAD_STATUS call set_seqint; /* let driver know */ cmp RETURN_1, SEND_SENSE jne complete; call add_scb_to_free_list; jmp await_busfree; complete: mov SCB_TAG call complete_post; jmp await_busfree; } complete_post: /* Post the SCBID in SINDEX and issue an interrupt */ call add_scb_to_free_list; mov ARG_1, SINDEX; if ((ahc->features & AHC_QUEUE_REGS) != 0) { mov A, SDSCB_QOFF; } else { mov A, QOUTPOS; } mvi QOUTFIFO_OFFSET call post_byte_setup; mov ARG_1 call post_byte; if ((ahc->features & AHC_QUEUE_REGS) == 0) { inc QOUTPOS; } mvi INTSTAT,CMDCMPLT ret; if ((ahc->flags & AHC_INITIATORROLE) != 0) { /* * Is it a disconnect message? Set a flag in the SCB to remind us * and await the bus going free. If this is an untagged transaction * store the SCB id for it in our untagged target table for lookup on * a reselection. */ mesgin_disconnect: /* * If ATN is raised, we still want to give the target a message. * Perhaps there was a parity error on this last message byte * or we want to abort this command. Either way, the target * should take us to message out phase and then attempt to * disconnect again. * XXX - Wait for more testing. test SCSISIGI, ATNI jnz mesgin_done; */ test SEQ_FLAGS, NOT_IDENTIFIED|NO_CDB_SENT jnz mesgin_proto_violation; or SCB_CONTROL,DISCONNECTED; if ((ahc->flags & AHC_PAGESCBS) != 0) { call add_scb_to_disc_list; } test SCB_CONTROL, TAG_ENB jnz await_busfree; mov ARG_1, SCB_TAG; mov SAVED_LUN, SCB_LUN; mov SCB_SCSIID call set_busy_target; jmp await_busfree; /* * Save data pointers message: * Copying RAM values back to SCB, for Save Data Pointers message, but * only if we've actually been into a data phase to change them. This * protects against bogus data in scratch ram and the residual counts * since they are only initialized when we go into data_in or data_out. * Ack the message as soon as possible. For chips without S/G pipelining, * we can only ack the message after SHADDR has been saved. On these * chips, SHADDR increments with every bus transaction, even PIO. */ mesgin_sdptrs: if ((ahc->features & AHC_ULTRA2) != 0) { mov NONE,SCSIDATL; /*dummy read from latch to ACK*/ test SEQ_FLAGS, DPHASE jz ITloop; } else { test SEQ_FLAGS, DPHASE jz mesgin_done; } /* * If we are asked to save our position at the end of the * transfer, just mark us at the end rather than perform a * full save. */ test SCB_RESIDUAL_SGPTR[0], SG_LIST_NULL jz mesgin_sdptrs_full; or SCB_SGPTR, SG_LIST_NULL; if ((ahc->features & AHC_ULTRA2) != 0) { jmp ITloop; } else { jmp mesgin_done; } mesgin_sdptrs_full: /* * The SCB_SGPTR becomes the next one we'll download, * and the SCB_DATAPTR becomes the current SHADDR. * Use the residual number since STCNT is corrupted by * any message transfer. */ if ((ahc->features & AHC_CMD_CHAN) != 0) { bmov SCB_DATAPTR, SHADDR, 4; if ((ahc->features & AHC_ULTRA2) == 0) { mov NONE,SCSIDATL; /*dummy read from latch to ACK*/ } bmov SCB_DATACNT, SCB_RESIDUAL_DATACNT, 8; } else { mvi DINDEX, SCB_DATAPTR; mvi SHADDR call bcopy_4; mov NONE,SCSIDATL; /*dummy read from latch to ACK*/ mvi SCB_RESIDUAL_DATACNT call bcopy_8; } jmp ITloop; /* * Restore pointers message? Data pointers are recopied from the * SCB anytime we enter a data phase for the first time, so all * we need to do is clear the DPHASE flag and let the data phase * code do the rest. We also reset/reallocate the FIFO to make * sure we have a clean start for the next data or command phase. */ mesgin_rdptrs: and SEQ_FLAGS, ~DPHASE; /* * We'll reload them * the next time through * the dataphase. */ or SXFRCTL0, CLRSTCNT|CLRCHN; jmp mesgin_done; /* * Index into our Busy Target table. SINDEX and DINDEX are modified * upon return. SCBPTR may be modified by this action. */ set_busy_target: shr DINDEX, 4, SINDEX; if ((ahc->flags & AHC_SCB_BTT) != 0) { mov SCBPTR, SAVED_LUN; add DINDEX, SCB_64_BTT; } else { add DINDEX, BUSY_TARGETS; } mov DINDIR, ARG_1 ret; /* * Identify message? For a reconnecting target, this tells us the lun * that the reconnection is for - find the correct SCB and switch to it, * clearing the "disconnected" bit so we don't "find" it by accident later. */ mesgin_identify: /* * Determine whether a target is using tagged or non-tagged * transactions by first looking at the transaction stored in * the busy target array. If there is no untagged transaction * for this target or the transaction is for a different lun, then * this must be a tagged transaction. */ shr SINDEX, 4, SAVED_SCSIID; and SAVED_LUN, MSG_IDENTIFY_LUNMASK, A; if ((ahc->flags & AHC_SCB_BTT) != 0) { add SINDEX, SCB_64_BTT; mov SCBPTR, SAVED_LUN; if ((ahc->flags & AHC_SEQUENCER_DEBUG) != 0) { add NONE, -SCB_64_BTT, SINDEX; jc . + 2; mvi INTSTAT, OUT_OF_RANGE; nop; add NONE, -(SCB_64_BTT + 16), SINDEX; jnc . + 2; mvi INTSTAT, OUT_OF_RANGE; nop; } } else { add SINDEX, BUSY_TARGETS; if ((ahc->flags & AHC_SEQUENCER_DEBUG) != 0) { add NONE, -BUSY_TARGETS, SINDEX; jc . + 2; mvi INTSTAT, OUT_OF_RANGE; nop; add NONE, -(BUSY_TARGETS + 16), SINDEX; jnc . + 2; mvi INTSTAT, OUT_OF_RANGE; nop; } } mov ARG_1, SINDIR; cmp ARG_1, SCB_LIST_NULL je snoop_tag; if ((ahc->flags & AHC_PAGESCBS) != 0) { mov ARG_1 call findSCB; } else { mov SCBPTR, ARG_1; } if ((ahc->flags & AHC_SCB_BTT) != 0) { jmp setup_SCB_id_lun_okay; } else { /* * We only allow one untagged command per-target * at a time. So, if the lun doesn't match, look * for a tag message. */ mov A, SCB_LUN; cmp SAVED_LUN, A je setup_SCB_id_lun_okay; if ((ahc->flags & AHC_PAGESCBS) != 0) { /* * findSCB removes the SCB from the * disconnected list, so we must replace * it there should this SCB be for another * lun. */ call cleanup_scb; } } /* * Here we "snoop" the bus looking for a SIMPLE QUEUE TAG message. * If we get one, we use the tag returned to find the proper * SCB. With SCB paging, we must search for non-tagged * transactions since the SCB may exist in any slot. If we're not * using SCB paging, we can use the tag as the direct index to the * SCB. */ snoop_tag: if ((ahc->flags & AHC_SEQUENCER_DEBUG) != 0) { or SEQ_FLAGS, 0x80; } mov NONE,SCSIDATL; /* ACK Identify MSG */ call phase_lock; if ((ahc->flags & AHC_SEQUENCER_DEBUG) != 0) { or SEQ_FLAGS, 0x1; } cmp LASTPHASE, P_MESGIN jne not_found; if ((ahc->flags & AHC_SEQUENCER_DEBUG) != 0) { or SEQ_FLAGS, 0x2; } cmp SCSIBUSL,MSG_SIMPLE_Q_TAG jne not_found; get_tag: if ((ahc->flags & AHC_PAGESCBS) != 0) { mvi ARG_1 call inb_next; /* tag value */ mov ARG_1 call findSCB; } else { mvi ARG_1 call inb_next; /* tag value */ mov SCBPTR, ARG_1; } /* * Ensure that the SCB the tag points to is for * an SCB transaction to the reconnecting target. */ setup_SCB: if ((ahc->flags & AHC_SEQUENCER_DEBUG) != 0) { or SEQ_FLAGS, 0x4; } mov A, SCB_SCSIID; cmp SAVED_SCSIID, A jne not_found_cleanup_scb; if ((ahc->flags & AHC_SEQUENCER_DEBUG) != 0) { or SEQ_FLAGS, 0x8; } setup_SCB_id_okay: mov A, SCB_LUN; cmp SAVED_LUN, A jne not_found_cleanup_scb; setup_SCB_id_lun_okay: if ((ahc->flags & AHC_SEQUENCER_DEBUG) != 0) { or SEQ_FLAGS, 0x10; } test SCB_CONTROL,DISCONNECTED jz not_found_cleanup_scb; and SCB_CONTROL,~DISCONNECTED; test SCB_CONTROL, TAG_ENB jnz setup_SCB_tagged; if ((ahc->flags & AHC_SCB_BTT) != 0) { mov A, SCBPTR; } mvi ARG_1, SCB_LIST_NULL; mov SAVED_SCSIID call set_busy_target; if ((ahc->flags & AHC_SCB_BTT) != 0) { mov SCBPTR, A; } setup_SCB_tagged: clr SEQ_FLAGS; /* make note of IDENTIFY */ call set_transfer_settings; /* See if the host wants to send a message upon reconnection */ test SCB_CONTROL, MK_MESSAGE jz mesgin_done; mvi HOST_MSG call mk_mesg; jmp mesgin_done; not_found_cleanup_scb: if ((ahc->flags & AHC_PAGESCBS) != 0) { call cleanup_scb; } not_found: mvi NO_MATCH call set_seqint; jmp mesgin_done; mk_mesg: if ((ahc->features & AHC_DT) == 0) { or SCSISIGO, ATNO, LASTPHASE; } else { mvi SCSISIGO, ATNO; } mov MSG_OUT,SINDEX ret; /* * Functions to read data in Automatic PIO mode. * * According to Adaptec's documentation, an ACK is not sent on input from * the target until SCSIDATL is read from. So we wait until SCSIDATL is * latched (the usual way), then read the data byte directly off the bus * using SCSIBUSL. When we have pulled the ATN line, or we just want to * acknowledge the byte, then we do a dummy read from SCISDATL. The SCSI * spec guarantees that the target will hold the data byte on the bus until * we send our ACK. * * The assumption here is that these are called in a particular sequence, * and that REQ is already set when inb_first is called. inb_{first,next} * use the same calling convention as inb. */ inb_next_wait_perr: mvi PERR_DETECTED call set_seqint; jmp inb_next_wait; inb_next: mov NONE,SCSIDATL; /*dummy read from latch to ACK*/ inb_next_wait: /* * If there is a parity error, wait for the kernel to * see the interrupt and prepare our message response * before continuing. */ test SSTAT1, REQINIT jz inb_next_wait; test SSTAT1, SCSIPERR jnz inb_next_wait_perr; inb_next_check_phase: and LASTPHASE, PHASE_MASK, SCSISIGI; cmp LASTPHASE, P_MESGIN jne mesgin_phasemis; inb_first: mov DINDEX,SINDEX; mov DINDIR,SCSIBUSL ret; /*read byte directly from bus*/ inb_last: mov NONE,SCSIDATL ret; /*dummy read from latch to ACK*/ } if ((ahc->flags & AHC_TARGETROLE) != 0) { /* * Change to a new phase. If we are changing the state of the I/O signal, * from out to in, wait an additional data release delay before continuing. */ change_phase: /* Wait for preceding I/O session to complete. */ test SCSISIGI, ACKI jnz .; /* Change the phase */ and DINDEX, IOI, SCSISIGI; mov SCSISIGO, SINDEX; and A, IOI, SINDEX; /* * If the data direction has changed, from * out (initiator driving) to in (target driving), * we must wait at least a data release delay plus * the normal bus settle delay. [SCSI III SPI 10.11.0] */ cmp DINDEX, A je change_phase_wait; test SINDEX, IOI jz change_phase_wait; call change_phase_wait; change_phase_wait: nop; nop; nop; nop ret; /* * Send a byte to an initiator in Automatic PIO mode. */ target_outb: or SXFRCTL0, SPIOEN; test SSTAT0, SPIORDY jz .; mov SCSIDATL, SINDEX; test SSTAT0, SPIORDY jz .; and SXFRCTL0, ~SPIOEN ret; } /* * Locate a disconnected SCB by SCBID. Upon return, SCBPTR and SINDEX will * be set to the position of the SCB. If the SCB cannot be found locally, * it will be paged in from host memory. RETURN_2 stores the address of the * preceding SCB in the disconnected list which can be used to speed up * removal of the found SCB from the disconnected list. */ if ((ahc->flags & AHC_PAGESCBS) != 0) { BEGIN_CRITICAL; findSCB: mov A, SINDEX; /* Tag passed in SINDEX */ cmp DISCONNECTED_SCBH, SCB_LIST_NULL je findSCB_notFound; mov SCBPTR, DISCONNECTED_SCBH; /* Initialize SCBPTR */ mvi ARG_2, SCB_LIST_NULL; /* Head of list */ jmp findSCB_loop; findSCB_next: cmp SCB_NEXT, SCB_LIST_NULL je findSCB_notFound; mov ARG_2, SCBPTR; mov SCBPTR,SCB_NEXT; findSCB_loop: cmp SCB_TAG, A jne findSCB_next; rem_scb_from_disc_list: cmp ARG_2, SCB_LIST_NULL je rHead; mov DINDEX, SCB_NEXT; mov SINDEX, SCBPTR; mov SCBPTR, ARG_2; mov SCB_NEXT, DINDEX; mov SCBPTR, SINDEX ret; rHead: mov DISCONNECTED_SCBH,SCB_NEXT ret; END_CRITICAL; findSCB_notFound: /* * We didn't find it. Page in the SCB. */ mov ARG_1, A; /* Save tag */ mov ALLZEROS call get_free_or_disc_scb; mvi DMAPARAMS, HDMAEN|DIRECTION|FIFORESET; mov ARG_1 jmp dma_scb; } /* * Prepare the hardware to post a byte to host memory given an * index of (A + (256 * SINDEX)) and a base address of SHARED_DATA_ADDR. */ post_byte_setup: mov ARG_2, SINDEX; if ((ahc->features & AHC_CMD_CHAN) != 0) { mvi DINDEX, CCHADDR; mvi SHARED_DATA_ADDR call set_1byte_addr; mvi CCHCNT, 1; mvi CCSCBCTL, CCSCBRESET ret; } else { mvi DINDEX, HADDR; mvi SHARED_DATA_ADDR call set_1byte_addr; mvi 1 call set_hcnt; mvi DFCNTRL, FIFORESET ret; } post_byte: if ((ahc->features & AHC_CMD_CHAN) != 0) { bmov CCSCBRAM, SINDEX, 1; or CCSCBCTL, CCSCBEN|CCSCBRESET; test CCSCBCTL, CCSCBDONE jz .; clr CCSCBCTL ret; } else { mov DFDAT, SINDEX; or DFCNTRL, HDMAEN|FIFOFLUSH; jmp dma_finish; } phase_lock_perr: mvi PERR_DETECTED call set_seqint; phase_lock: /* * If there is a parity error, wait for the kernel to * see the interrupt and prepare our message response * before continuing. */ test SSTAT1, REQINIT jz phase_lock; test SSTAT1, SCSIPERR jnz phase_lock_perr; phase_lock_latch_phase: if ((ahc->features & AHC_DT) == 0) { and SCSISIGO, PHASE_MASK, SCSISIGI; } and LASTPHASE, PHASE_MASK, SCSISIGI ret; if ((ahc->features & AHC_CMD_CHAN) == 0) { set_hcnt: mov HCNT[0], SINDEX; clear_hcnt: clr HCNT[1]; clr HCNT[2] ret; set_stcnt_from_hcnt: mov STCNT[0], HCNT[0]; mov STCNT[1], HCNT[1]; mov STCNT[2], HCNT[2] ret; bcopy_8: mov DINDIR, SINDIR; bcopy_7: mov DINDIR, SINDIR; mov DINDIR, SINDIR; bcopy_5: mov DINDIR, SINDIR; bcopy_4: mov DINDIR, SINDIR; bcopy_3: mov DINDIR, SINDIR; mov DINDIR, SINDIR; mov DINDIR, SINDIR ret; } if ((ahc->flags & AHC_TARGETROLE) != 0) { /* * Setup addr assuming that A is an index into * an array of 32byte objects, SINDEX contains * the base address of that array, and DINDEX * contains the base address of the location * to store the indexed address. */ set_32byte_addr: shr ARG_2, 3, A; shl A, 5; jmp set_1byte_addr; } /* * Setup addr assuming that A is an index into * an array of 64byte objects, SINDEX contains * the base address of that array, and DINDEX * contains the base address of the location * to store the indexed address. */ set_64byte_addr: shr ARG_2, 2, A; shl A, 6; /* * Setup addr assuming that A + (ARG_2 * 256) is an * index into an array of 1byte objects, SINDEX contains * the base address of that array, and DINDEX contains * the base address of the location to store the computed * address. */ set_1byte_addr: add DINDIR, A, SINDIR; mov A, ARG_2; adc DINDIR, A, SINDIR; clr A; adc DINDIR, A, SINDIR; adc DINDIR, A, SINDIR ret; /* * Either post or fetch an SCB from host memory based on the * DIRECTION bit in DMAPARAMS. The host SCB index is in SINDEX. */ dma_scb: mov A, SINDEX; if ((ahc->features & AHC_CMD_CHAN) != 0) { mvi DINDEX, CCHADDR; mvi HSCB_ADDR call set_64byte_addr; mov CCSCBPTR, SCBPTR; test DMAPARAMS, DIRECTION jz dma_scb_tohost; if ((ahc->flags & AHC_SCB_BTT) != 0) { mvi CCHCNT, SCB_DOWNLOAD_SIZE_64; } else { mvi CCHCNT, SCB_DOWNLOAD_SIZE; } mvi CCSCBCTL, CCARREN|CCSCBEN|CCSCBDIR|CCSCBRESET; cmp CCSCBCTL, CCSCBDONE|ARRDONE|CCARREN|CCSCBEN|CCSCBDIR jne .; jmp dma_scb_finish; dma_scb_tohost: mvi CCHCNT, SCB_UPLOAD_SIZE; if ((ahc->features & AHC_ULTRA2) == 0) { mvi CCSCBCTL, CCSCBRESET; bmov CCSCBRAM, SCB_BASE, SCB_UPLOAD_SIZE; or CCSCBCTL, CCSCBEN|CCSCBRESET; test CCSCBCTL, CCSCBDONE jz .; } else if ((ahc->bugs & AHC_SCBCHAN_UPLOAD_BUG) != 0) { mvi CCSCBCTL, CCARREN|CCSCBRESET; cmp CCSCBCTL, ARRDONE|CCARREN jne .; mvi CCHCNT, SCB_UPLOAD_SIZE; mvi CCSCBCTL, CCSCBEN|CCSCBRESET; cmp CCSCBCTL, CCSCBDONE|CCSCBEN jne .; } else { mvi CCSCBCTL, CCARREN|CCSCBEN|CCSCBRESET; cmp CCSCBCTL, CCSCBDONE|ARRDONE|CCARREN|CCSCBEN jne .; } dma_scb_finish: clr CCSCBCTL; test CCSCBCTL, CCARREN|CCSCBEN jnz .; ret; } else { mvi DINDEX, HADDR; mvi HSCB_ADDR call set_64byte_addr; mvi SCB_DOWNLOAD_SIZE call set_hcnt; mov DFCNTRL, DMAPARAMS; test DMAPARAMS, DIRECTION jnz dma_scb_fromhost; /* Fill it with the SCB data */ copy_scb_tofifo: mvi SINDEX, SCB_BASE; add A, SCB_DOWNLOAD_SIZE, SINDEX; copy_scb_tofifo_loop: call copy_to_fifo_8; cmp SINDEX, A jne copy_scb_tofifo_loop; or DFCNTRL, HDMAEN|FIFOFLUSH; jmp dma_finish; dma_scb_fromhost: mvi DINDEX, SCB_BASE; if ((ahc->bugs & AHC_PCI_2_1_RETRY_BUG) != 0) { /* * The PCI module will only issue a PCI * retry if the data FIFO is empty. If the * host disconnects in the middle of a * transfer, we must empty the fifo of all * available data to force the chip to * continue the transfer. This does not * happen for SCSI transfers as the SCSI module * will drain the FIFO as data are made available. * When the hang occurs, we know that a multiple * of 8 bytes is in the FIFO because the PCI * module has an 8 byte input latch that only * dumps to the FIFO when HCNT == 0 or the * latch is full. */ clr A; /* Wait for at least 8 bytes of data to arrive. */ dma_scb_hang_fifo: test DFSTATUS, FIFOQWDEMP jnz dma_scb_hang_fifo; dma_scb_hang_wait: test DFSTATUS, MREQPEND jnz dma_scb_hang_wait; test DFSTATUS, HDONE jnz dma_scb_hang_dma_done; test DFSTATUS, HDONE jnz dma_scb_hang_dma_done; test DFSTATUS, HDONE jnz dma_scb_hang_dma_done; /* * The PCI module no longer intends to perform * a PCI transaction. Drain the fifo. */ dma_scb_hang_dma_drain_fifo: not A, HCNT; add A, SCB_DOWNLOAD_SIZE+SCB_BASE+1; and A, ~0x7; mov DINDIR,DFDAT; cmp DINDEX, A jne . - 1; cmp DINDEX, SCB_DOWNLOAD_SIZE+SCB_BASE je dma_finish_nowait; /* Restore A as the lines left to transfer. */ add A, -SCB_BASE, DINDEX; shr A, 3; jmp dma_scb_hang_fifo; dma_scb_hang_dma_done: and DFCNTRL, ~HDMAEN; test DFCNTRL, HDMAEN jnz .; add SEQADDR0, A; } else { call dma_finish; } call dfdat_in_8; call dfdat_in_8; call dfdat_in_8; dfdat_in_8: mov DINDIR,DFDAT; dfdat_in_7: mov DINDIR,DFDAT; mov DINDIR,DFDAT; mov DINDIR,DFDAT; mov DINDIR,DFDAT; mov DINDIR,DFDAT; dfdat_in_2: mov DINDIR,DFDAT; mov DINDIR,DFDAT ret; } copy_to_fifo_8: mov DFDAT,SINDIR; mov DFDAT,SINDIR; copy_to_fifo_6: mov DFDAT,SINDIR; copy_to_fifo_5: mov DFDAT,SINDIR; copy_to_fifo_4: mov DFDAT,SINDIR; mov DFDAT,SINDIR; mov DFDAT,SINDIR; mov DFDAT,SINDIR ret; /* * Wait for DMA from host memory to data FIFO to complete, then disable * DMA and wait for it to acknowledge that it's off. */ dma_finish: test DFSTATUS,HDONE jz dma_finish; dma_finish_nowait: /* Turn off DMA */ and DFCNTRL, ~HDMAEN; test DFCNTRL, HDMAEN jnz .; ret; /* * Restore an SCB that failed to match an incoming reselection * to the correct/safe state. If the SCB is for a disconnected * transaction, it must be returned to the disconnected list. * If it is not in the disconnected state, it must be free. */ cleanup_scb: if ((ahc->flags & AHC_PAGESCBS) != 0) { test SCB_CONTROL,DISCONNECTED jnz add_scb_to_disc_list; } add_scb_to_free_list: if ((ahc->flags & AHC_PAGESCBS) != 0) { BEGIN_CRITICAL; mov SCB_NEXT, FREE_SCBH; mvi SCB_TAG, SCB_LIST_NULL; mov FREE_SCBH, SCBPTR ret; END_CRITICAL; } else { mvi SCB_TAG, SCB_LIST_NULL ret; } if ((ahc->flags & AHC_39BIT_ADDRESSING) != 0) { set_hhaddr: or DSCOMMAND1, HADDLDSEL0; and HADDR, SG_HIGH_ADDR_BITS, SINDEX; and DSCOMMAND1, ~HADDLDSEL0 ret; } if ((ahc->flags & AHC_PAGESCBS) != 0) { get_free_or_disc_scb: BEGIN_CRITICAL; cmp FREE_SCBH, SCB_LIST_NULL jne dequeue_free_scb; cmp DISCONNECTED_SCBH, SCB_LIST_NULL jne dequeue_disc_scb; return_error: mvi NO_FREE_SCB call set_seqint; mvi SINDEX, SCB_LIST_NULL ret; dequeue_disc_scb: mov SCBPTR, DISCONNECTED_SCBH; mov DISCONNECTED_SCBH, SCB_NEXT; END_CRITICAL; mvi DMAPARAMS, FIFORESET; mov SCB_TAG jmp dma_scb; BEGIN_CRITICAL; dequeue_free_scb: mov SCBPTR, FREE_SCBH; mov FREE_SCBH, SCB_NEXT ret; END_CRITICAL; add_scb_to_disc_list: /* * Link this SCB into the DISCONNECTED list. This list holds the * candidates for paging out an SCB if one is needed for a new command. * Modifying the disconnected list is a critical(pause dissabled) section. */ BEGIN_CRITICAL; mov SCB_NEXT, DISCONNECTED_SCBH; mov DISCONNECTED_SCBH, SCBPTR ret; END_CRITICAL; } set_seqint: mov INTSTAT, SINDEX; nop; return: ret; @ 1.23 log @fix various typos in comments. @ text @d43 1 a43 1 VERSION = "$NetBSD: aic7xxx.seq,v 1.22 2022/01/25 22:01:34 andvar Exp $" d60 1 a60 1 * use byte 27 of the SCB as a psuedo-next pointer and to thread a list @ 1.22 log @fix various typos in comments. @ text @d43 1 a43 1 VERSION = "$NetBSD: aic7xxx.seq,v 1.21 2021/09/03 22:33:17 andvar Exp $" d1651 1 a1651 1 * See if we attempted to deliver a message but the target ingnored us. @ 1.21 log @s/existant/existent/ in comments and messages, plus few more similar fixes. @ text @d43 1 a43 1 VERSION = "$NetBSD: aic7xxx.seq,v 1.20 2019/06/04 10:15:22 msaitoh Exp $" d1479 1 a1479 1 * indentify message sequence and send it to the target. The host may d1489 1 a1489 1 * sequence while protecting the seqencer from testing the MK_MESSAGE bit d1491 1 a1491 1 * BDR message in responce to a bad reselection would leave us pointed to d1699 1 a1699 1 * a reselction. @ 1.20 log @ Fix typo (s/recevie/receive/). @ text @d43 1 a43 1 VERSION = "$NetBSD: aic7xxx.seq,v 1.19 2014/10/18 08:33:28 snj Exp $" d754 1 a754 1 * so we don't end up referencing a non-existant page. @ 1.19 log @src is too big these days to tolerate superfluous apostrophes. It's "its", people! @ text @d43 1 a43 1 VERSION = "$NetBSD: aic7xxx.seq,v 1.18 2006/11/25 16:48:32 christos Exp $" d1643 1 a1643 1 * If we recevied good status but never successfully sent the @ 1.19.20.1 log @Sync with HEAD @ text @d43 1 a43 1 VERSION = "$NetBSD: aic7xxx.seq,v 1.20 2019/06/04 10:15:22 msaitoh Exp $" d1643 1 a1643 1 * If we received good status but never successfully sent the @ 1.18 log @spell precede; from Zafer @ text @d43 1 a43 1 VERSION = "$NetBSD: aic7xxx.seq,v 1.17 2005/12/11 12:22:18 christos Exp $" d322 1 a322 1 * run it's own target mode message state engine. d1448 1 a1448 1 * Wait for our ACK to go-away on it's own d1570 1 a1570 1 * run it's own message state engine. To avoid an @ 1.18.98.1 log @update from HEAD @ text @d43 1 a43 1 VERSION = "$NetBSD$" d322 1 a322 1 * run its own target mode message state engine. d1448 1 a1448 1 * Wait for our ACK to go-away on its own d1570 1 a1570 1 * run its own message state engine. To avoid an @ 1.17 log @merge ktrace-lwp. @ text @d43 1 a43 1 VERSION = "$NetBSD: aic7xxx.seq,v 1.15.2.1 2005/03/04 16:43:50 skrll Exp $" d2005 1 a2005 1 /* Wait for preceeding I/O session to complete. */ @ 1.17.20.1 log @Sync with head. @ text @d43 1 a43 1 VERSION = "$NetBSD: aic7xxx.seq,v 1.18 2006/11/25 16:48:32 christos Exp $" d2005 1 a2005 1 /* Wait for preceding I/O session to complete. */ @ 1.17.22.1 log @sync with head. @ text @d43 1 a43 1 VERSION = "$NetBSD: aic7xxx.seq,v 1.17 2005/12/11 12:22:18 christos Exp $" d2005 1 a2005 1 /* Wait for preceding I/O session to complete. */ @ 1.16 log @nuke trailing whitespace @ text @d43 1 a43 1 VERSION = "$NetBSD: aic7xxx.seq,v 1.15 2003/05/03 18:11:31 wiz Exp $" @ 1.16.4.1 log @sync with head. @ text @d43 1 a43 1 VERSION = "$NetBSD: aic7xxx.seq,v 1.16 2005/02/27 00:27:23 perry Exp $" d2005 1 a2005 1 /* Wait for preceding I/O session to complete. */ @ 1.15 log @DMA, not dma nor Dma. @ text @d43 1 a43 1 VERSION = "$NetBSD: aic7xxx.seq,v 1.14 2003/04/19 19:26:10 fvdl Exp $" d59 1 a59 1 * create yet another SCB waiting for selection. The solution used here is to d61 1 a61 1 * of SCBs that are awaiting selection. Since 0-0xfe are valid SCB indexes, d104 1 a104 1 * We have at least one queued SCB now and we don't have any d282 1 a282 1 d399 1 a399 1 } d411 1 a411 1 * We carefully toggle SPIOEN to allow us to return the d479 1 a479 1 d497 1 a497 1 test SEQ_FLAGS, NO_DISCONNECT jz target_disconnect; d518 1 a518 1 d556 1 a556 1 tqinfifo_has_space: d618 1 a618 1 d686 1 a686 1 d793 1 a793 1 * be transferred using memory write and invalidate PCI transactions. d1005 1 a1005 1 adc SCB_RESIDUAL_SGPTR[2], -1; d1144 1 a1144 1 * data fifo is full, but the PCI input latch is not empty, d1160 1 a1160 1 * Advance the scatter-gather pointers if needed d1415 1 a1415 1 bmov DFDAT, SCB_CDB_STORE, 12; d1430 1 a1430 1 bmov DFDAT, SCB_CDB_STORE, 12; d1486 1 a1486 1 * d1609 1 a1609 1 * process this information. In the case of a non zero status byte, we d1613 1 a1613 1 * it to the QINFIFO and tell us not to post to the QOUTFIFO by setting d1634 1 a1634 1 test SEQ_FLAGS, NOT_IDENTIFIED|NO_CDB_SENT jz complete_accepted; d2038 1 a2038 1 d2112 1 a2112 1 phase_lock: @ 1.15.10.1 log @sync with -current @ text @d43 1 a43 1 VERSION = "$NetBSD: aic7xxx.seq,v 1.16 2005/02/27 00:27:23 perry Exp $" d59 1 a59 1 * create yet another SCB waiting for selection. The solution used here is to d61 1 a61 1 * of SCBs that are awaiting selection. Since 0-0xfe are valid SCB indexes, d104 1 a104 1 * We have at least one queued SCB now and we don't have any d282 1 a282 1 d399 1 a399 1 } d411 1 a411 1 * We carefully toggle SPIOEN to allow us to return the d479 1 a479 1 d497 1 a497 1 test SEQ_FLAGS, NO_DISCONNECT jz target_disconnect; d518 1 a518 1 d556 1 a556 1 tqinfifo_has_space: d618 1 a618 1 d686 1 a686 1 d793 1 a793 1 * be transferred using memory write and invalidate PCI transactions. d1005 1 a1005 1 adc SCB_RESIDUAL_SGPTR[2], -1; d1144 1 a1144 1 * data fifo is full, but the PCI input latch is not empty, d1160 1 a1160 1 * Advance the scatter-gather pointers if needed d1415 1 a1415 1 bmov DFDAT, SCB_CDB_STORE, 12; d1430 1 a1430 1 bmov DFDAT, SCB_CDB_STORE, 12; d1486 1 a1486 1 * d1609 1 a1609 1 * process this information. In the case of a non zero status byte, we d1613 1 a1613 1 * it to the QINFIFO and tell us not to post to the QOUTFIFO by setting d1634 1 a1634 1 test SEQ_FLAGS, NOT_IDENTIFIED|NO_CDB_SENT jz complete_accepted; d2038 1 a2038 1 d2112 1 a2112 1 phase_lock: @ 1.15.12.1 log @sync with head. xen and whitespace. xen part is not finished. @ text @d43 1 a43 1 VERSION = "$NetBSD: aic7xxx.seq,v 1.15 2003/05/03 18:11:31 wiz Exp $" d59 1 a59 1 * create yet another SCB waiting for selection. The solution used here is to d61 1 a61 1 * of SCBs that are awaiting selection. Since 0-0xfe are valid SCB indexes, d104 1 a104 1 * We have at least one queued SCB now and we don't have any d282 1 a282 1 d399 1 a399 1 } d411 1 a411 1 * We carefully toggle SPIOEN to allow us to return the d479 1 a479 1 d497 1 a497 1 test SEQ_FLAGS, NO_DISCONNECT jz target_disconnect; d518 1 a518 1 d556 1 a556 1 tqinfifo_has_space: d618 1 a618 1 d686 1 a686 1 d793 1 a793 1 * be transferred using memory write and invalidate PCI transactions. d1005 1 a1005 1 adc SCB_RESIDUAL_SGPTR[2], -1; d1144 1 a1144 1 * data fifo is full, but the PCI input latch is not empty, d1160 1 a1160 1 * Advance the scatter-gather pointers if needed d1415 1 a1415 1 bmov DFDAT, SCB_CDB_STORE, 12; d1430 1 a1430 1 bmov DFDAT, SCB_CDB_STORE, 12; d1486 1 a1486 1 * d1609 1 a1609 1 * process this information. In the case of a non zero status byte, we d1613 1 a1613 1 * it to the QINFIFO and tell us not to post to the QOUTFIFO by setting d1634 1 a1634 1 test SEQ_FLAGS, NOT_IDENTIFIED|NO_CDB_SENT jz complete_accepted; d2038 1 a2038 1 d2112 1 a2112 1 phase_lock: @ 1.15.2.1 log @Sync with HEAD. Hi Perry! @ text @d43 1 a43 1 VERSION = "$NetBSD: aic7xxx.seq,v 1.16 2005/02/27 00:27:23 perry Exp $" d59 1 a59 1 * create yet another SCB waiting for selection. The solution used here is to d61 1 a61 1 * of SCBs that are awaiting selection. Since 0-0xfe are valid SCB indexes, d104 1 a104 1 * We have at least one queued SCB now and we don't have any d282 1 a282 1 d399 1 a399 1 } d411 1 a411 1 * We carefully toggle SPIOEN to allow us to return the d479 1 a479 1 d497 1 a497 1 test SEQ_FLAGS, NO_DISCONNECT jz target_disconnect; d518 1 a518 1 d556 1 a556 1 tqinfifo_has_space: d618 1 a618 1 d686 1 a686 1 d793 1 a793 1 * be transferred using memory write and invalidate PCI transactions. d1005 1 a1005 1 adc SCB_RESIDUAL_SGPTR[2], -1; d1144 1 a1144 1 * data fifo is full, but the PCI input latch is not empty, d1160 1 a1160 1 * Advance the scatter-gather pointers if needed d1415 1 a1415 1 bmov DFDAT, SCB_CDB_STORE, 12; d1430 1 a1430 1 bmov DFDAT, SCB_CDB_STORE, 12; d1486 1 a1486 1 * d1609 1 a1609 1 * process this information. In the case of a non zero status byte, we d1613 1 a1613 1 * it to the QINFIFO and tell us not to post to the QOUTFIFO by setting d1634 1 a1634 1 test SEQ_FLAGS, NOT_IDENTIFIED|NO_CDB_SENT jz complete_accepted; d2038 1 a2038 1 d2112 1 a2112 1 phase_lock: @ 1.14 log @Newer aic sequencer assembler. From FreeBSD, written by Justin Gibbs. Ported to NetBSD by Pascal Renauld at Network Storage Solutions, Inc. Some Makefile changes by me. The 79xx code isn't yet used, driver for that to follow shortly. @ text @d43 1 a43 1 VERSION = "$NetBSD$" d600 1 a600 1 * perspective of the initiator. The dma d1260 1 a1260 1 * the dma code again. In the case of ULTRA2 adapters, @ 1.13 log @precede, not preceed. @ text @a0 2 /* $NetBSD: aic7xxx.seq,v 1.12 2001/06/19 13:45:57 wiz Exp $ */ d4 2 a5 1 * Copyright (c) 1994-2000 Justin Gibbs. d14 8 a21 2 * 2. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. d24 2 a25 1 * the GNU Public License ("GPL"). d27 6 a32 5 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL d35 4 a38 4 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. d40 1 a40 1 * $FreeBSD: src/sys/dev/aic7xxx/aic7xxx.seq,v 1.94 2000/02/09 21:25:00 gibbs Exp $ d43 4 a46 3 /* * #ifdef __NetBSD__ */ a48 8 /* * Assembler can't handle ifdef. * * #else * #include * #include * #endif */ d68 7 a74 12 reset: clr SCSISIGO; /* De-assert BSY */ mvi MSG_OUT, MSG_NOOP; /* No message to send */ and SXFRCTL1, ~BITBUCKET; /* Always allow reselection */ and SCSISEQ, ENSELI|ENRSELI|ENAUTOATNP, SCSISEQ_TEMPLATE; if ((ahc->features & AHC_CMD_CHAN) != 0) { /* Ensure that no DMA operations are in progress */ clr CCSGCTL; clr CCSCBCTL; } d78 7 a84 2 if ((ahc->features & AHC_QUEUE_REGS) == 0) { mov A, QINPOS; d86 1 a87 5 if ((ahc->features & AHC_QUEUE_REGS) == 0) { and SEQCTL, ~PAUSEDIS; } test SSTAT0, SELDO|SELDI jnz selection; test SCSISEQ, ENSELO jnz poll_for_work; a88 6 /* * Twin channel devices cannot handle things like SELTO * interrupts on the "background" channel. So, if we * are selecting, keep polling the current channel util * either a selection or reselection occurs. */ a89 3 test SSTAT0, SELDO|SELDI jnz selection; test SCSISEQ, ENSELO jnz poll_for_work; xor SBLKCTL,SELBUSB; /* Toggle back */ d91 1 a91 1 cmp WAITING_SCBH,SCB_LIST_NULL jne start_waiting; d94 1 a96 2 mov NONE, SNSCB_QOFF; inc QINPOS; d98 1 a98 1 or SEQCTL, PAUSEDIS; a99 2 inc QINPOS; and SEQCTL, ~PAUSEDIS; d101 1 d103 5 a107 6 /* * We have at least one queued SCB now and we don't have any * SCBs in the list of SCBs awaiting selection. If we have * any SCBs available for use, pull the tag from the QINFIFO * and get to work on it. */ d110 1 a110 7 } dequeue_scb: add A, -1, QINPOS; mvi QINFIFO_OFFSET call fetch_byte; if ((ahc->flags & AHC_PAGESCBS) == 0) { d112 1 a112 1 mov SCBPTR, RETURN_2; d114 2 d117 3 a119 3 /* * DMA the SCB from host ram into the current SCB location. */ d121 1 a121 18 mov RETURN_2 call dma_scb; /* * Preset the residual fields in case we never go through a data phase. * This isn't done by the host so we can avoid a DMA to clear these * fields for the normal case of I/O that completes without underrun * or overrun conditions. */ if ((ahc->features & AHC_CMD_CHAN) != 0) { bmov SCB_RESID_DCNT, SCB_DATACNT, 3; } else { mov SCB_RESID_DCNT[0],SCB_DATACNT[0]; mov SCB_RESID_DCNT[1],SCB_DATACNT[1]; mov SCB_RESID_DCNT[2],SCB_DATACNT[2]; } mov SCB_RESID_SGCNT, SCB_SGCOUNT; start_scb: d123 4 a126 2 * Place us on the waiting list in case our selection * doesn't win during bus arbitration. d128 8 d138 7 d147 1 a147 1 * Pull the first entry off of the waiting SCB list. a150 1 jmp poll_for_work; d152 8 a159 30 start_selection: if ((ahc->features & AHC_TWIN) != 0) { and SINDEX,~SELBUSB,SBLKCTL;/* Clear channel select bit */ and A,SELBUSB,SCB_TCL; /* Get new channel bit */ or SINDEX,A; mov SBLKCTL,SINDEX; /* select channel */ } initialize_scsiid: mov SINDEX, SCSISEQ_TEMPLATE; if ((ahc->flags & AHC_TARGETMODE) != 0) { test SCB_CONTROL, TARGET_SCB jz . + 4; if ((ahc->features & AHC_ULTRA2) != 0) { mov SCSIID_ULTRA2, SCB_CMDPTR[2]; } else { mov SCSIID, SCB_CMDPTR[2]; } or SINDEX, TEMODE; jmp initialize_scsiid_fini; } if ((ahc->features & AHC_ULTRA2) != 0) { and A, TID, SCB_TCL; /* Get target ID */ and SCSIID_ULTRA2, OID; /* Clear old target */ or SCSIID_ULTRA2, A; } else { and A, TID, SCB_TCL; /* Get target ID */ and SCSIID, OID; /* Clear old target */ or SCSIID, A; } initialize_scsiid_fini: mov SCSISEQ, SINDEX ret; d161 8 a168 20 /* * Initialize transfer settings and clear the SCSI channel. * SINDEX should contain any additional bit's the client wants * set in SXFRCTL0. We also assume that the current SCB is * a valid SCB for the target we wish to talk to. */ initialize_channel: or SXFRCTL0, CLRSTCNT|CLRCHN, SINDEX; set_transfer_settings: if ((ahc->features & AHC_ULTRA) != 0) { test SCB_CONTROL, ULTRAENB jz . + 2; or SXFRCTL0, FAST20; } /* * Initialize SCSIRATE with the appropriate value for this target. */ if ((ahc->features & AHC_ULTRA2) != 0) { bmov SCSIRATE, SCB_SCSIRATE, 2 ret; } else { mov SCSIRATE, SCB_SCSIRATE ret; d171 6 a176 1 selection: a177 1 mvi CLRSINT0, CLRSELDI; d179 2 a180 2 if ((ahc->flags & AHC_TARGETMODE) != 0) { if ((ahc->flags & AHC_INITIATORMODE) != 0) { d183 1 a190 1 mvi CLRSINT1, CLRBUSFREE; d196 1 a196 1 or SEQ_FLAGS, CMDPHASE_PENDING; d201 1 a201 1 mvi TMODE_CMDADDR call set_32byte_addr; d205 1 a205 1 mvi TMODE_CMDADDR call set_32byte_addr; d210 6 a215 3 and SAVED_TCL, SELID_MASK, SELID; if ((ahc->features & AHC_CMD_CHAN) != 0) { mov CCSCBRAM, SAVED_TCL; d217 6 a222 1 mov DFDAT, SAVED_TCL; a223 2 /* The Target ID we were selected at */ d225 1 a225 7 if ((ahc->features & AHC_MULTI_TID) != 0) { and CCSCBRAM, OID, TARGIDIN; } else if ((ahc->features & AHC_ULTRA2) != 0) { and CCSCBRAM, OID, SCSIID_ULTRA2; } else { and CCSCBRAM, OID, SCSIID; } d227 1 a227 7 if ((ahc->features & AHC_MULTI_TID) != 0) { and DFDAT, OID, TARGIDIN; } else if ((ahc->features & AHC_ULTRA2) != 0) { and DFDAT, OID, SCSIID_ULTRA2; } else { and DFDAT, OID, SCSIID; } a229 3 /* No tag yet */ mvi INITIATOR_TAG, SCB_LIST_NULL; d233 2 a248 1 /* XXX May need to be more lax here for older initiators... */ d256 1 d272 1 a272 1 jnc ident_messages_done; d274 2 a275 1 jc ident_messages_done; d300 1 a300 12 jmp ident_messages_done; /* * Pushed message loop to allow the kernel to * run it's own target mode message state engine. */ host_target_message_loop: mvi INTSTAT, HOST_MSG_LOOP; nop; cmp RETURN_1, EXIT_MSG_LOOP je target_ITloop; test SSTAT0, SPIORDY jz .; jmp host_target_message_loop; a302 10 /* If ring buffer is full, return busy or queue full */ mov A, KERNEL_TQINPOS; cmp TQINPOS, A jne tqinfifo_has_space; mvi P_STATUS|BSYO call change_phase; cmp INITIATOR_TAG, SCB_LIST_NULL je . + 3; mvi STATUS_QUEUE_FULL call target_outb; jmp target_busfree_wait; mvi STATUS_BUSY call target_outb; jmp target_busfree_wait; tqinfifo_has_space: d309 4 a312 2 or SEQ_FLAGS, TARG_CMD_PENDING|IDENTIFY_SEEN; test SCSISIGI, ATNI jnz target_mesgout_pending_msg; d314 6 a319 7 /* * We carefully toggle SPIOEN to allow us to return the * message byte we receive so it can be checked prior to * driving REQ on the bus for the next byte. */ target_inb: d321 2 a322 1 * Drive REQ on the bus by enabling SCSI PIO. d324 3 a326 2 or SXFRCTL0, SPIOEN; /* Wait for the byte */ d328 1 a328 4 /* Prevent our read from triggering another REQ */ and SXFRCTL0, ~SPIOEN; /* Save latched contents */ mov DINDEX, SCSIDATL ret; d331 1 a331 1 if ((ahc->flags & AHC_INITIATORMODE) != 0) { d338 8 a345 1 and SAVED_TCL, SELID_MASK, SELID; d348 1 a348 1 or SAVED_TCL, SELBUSB; d350 1 a350 8 or SXFRCTL0, SPIOEN|CLRSTCNT|CLRCHN; mvi CLRSINT1,CLRBUSFREE; or SIMODE1, ENBUSFREE; /* * We aren't expecting a * bus free, so interrupt * the kernel driver if it * happens. */ d354 74 d436 1 a436 2 and SCSISEQ, ENSELI|ENRSELI|ENAUTOATNP, SCSISEQ_TEMPLATE; mvi CLRSINT0, CLRSELDO; d439 4 a442 2 mov SAVED_TCL, SCB_TCL; if ((ahc->flags & AHC_TARGETMODE) != 0) { d445 8 d459 1 a459 1 mvi CLRSINT1,CLRBUSFREE; d464 1 a464 2 and A, LID, SCB_TCL; or A, MSG_IDENTIFYFLAG call target_outb; d472 1 a472 2 mov SCB_INITIATOR_TAG call target_outb; mov INITIATOR_TAG, SCB_INITIATOR_TAG; d480 3 a482 1 d498 2 d503 1 a503 3 mov RETURN_1, ALLZEROS; call complete_target_cmd; cmp RETURN_1, CONT_MSG_LOOP jne .; d505 3 a507 1 mov SCB_TAG call dma_scb; d512 1 d514 2 a516 1 target_mesgout_pending_msg: d529 4 d539 18 d576 1 a576 1 add SINDEX, TARG_SCSIRATE, A; d580 1 a581 1 or SXFRCTL0, SPIOEN; d599 5 a603 2 * Data direction flags are from the * perspective of the initiator. d605 2 a606 7 test SCB_TARGET_PHASES[1], TARGET_DATA_IN jz . + 4; mvi LASTPHASE, P_DATAOUT; mvi P_DATAIN|BSYO call change_phase; jmp . + 3; mvi LASTPHASE, P_DATAIN; mvi P_DATAOUT|BSYO call change_phase; mov ALLZEROS call initialize_channel; d612 1 a612 1 mov SCB_TARGET_STATUS call target_outb; d635 1 a635 3 mvi HCNT[0], 28; clr HCNT[1]; clr HCNT[2]; d643 1 a643 1 if ((ahc->flags & AHC_INITIATORMODE) != 0) { d645 1 a645 9 mvi SPIOEN call initialize_channel; /* * We aren't expecting a bus free, so interrupt * the kernel driver if it happens. */ mvi CLRSINT1,CLRBUSFREE; or SIMODE1, ENBUSFREE; d652 2 a653 1 or SEQ_FLAGS, IDENTIFY_SEEN; d660 1 d672 1 a672 1 mvi INTSTAT,BAD_PHASE; d678 3 d684 1 a684 1 mvi INTSTAT, BAD_PHASE; d694 1 d705 3 a707 1 and SXFRCTL0, ~(FAST20); d711 40 a750 1 clr SEQ_FLAGS ret; d752 39 d792 3 a794 2 * If we re-enter the data phase after going through another phase, the * STCNT may have been cleared, so restore it from the residual field. d796 27 a822 17 data_phase_reinit: if ((ahc->features & AHC_ULTRA2) != 0) { /* * The preload circuitry requires us to * reload the address too, so pull it from * the shaddow address. */ bmov HADDR, SHADDR, 4; bmov HCNT, SCB_RESID_DCNT, 3; } else if ((ahc->features & AHC_CMD_CHAN) != 0) { bmov STCNT, SCB_RESID_DCNT, 3; } else { mvi DINDEX, STCNT; mvi SCB_RESID_DCNT call bcopy_3; } and DATA_COUNT_ODD, 0x1, SCB_RESID_DCNT[0]; jmp data_phase_loop; d825 3 a834 4 call assert; /* * Ensure entering a data * phase is okay - seen identify, etc. */ d836 2 a837 1 mvi CCSGADDR, CCSGADDR_MAX; d839 1 a839 1 test SEQ_FLAGS, DPHASE jnz data_phase_reinit; d841 11 a851 1 /* We have seen a data phase */ d856 4 a859 3 * Also set SG_COUNT and SG_NEXT in memory since we cannot * modify the values in the SCB itself until we see a * save data pointers message. d861 4 d867 1 d871 5 d877 2 a878 1 and DATA_COUNT_ODD, 0x1, SCB_DATACNT[0]; d888 3 a890 6 if ((ahc->features & AHC_CMD_CHAN) != 0) { bmov SG_COUNT, SCB_SGCOUNT, 5; } else { mvi DINDEX, SG_COUNT; mvi SCB_SGCOUNT call bcopy_5; } d892 6 a897 9 data_phase_loop: /* Guard against overruns */ test SG_COUNT, 0xff jnz data_phase_inbounds; /* * Turn on 'Bit Bucket' mode, set the transfer count to * 16meg and let the target run until it changes phase. * When the transfer completes, notify the host that we * had an overrun. */ d899 2 a900 5 and DMAPARAMS, ~(HDMAEN|SDMAEN); if ((ahc->features & AHC_ULTRA2) != 0) { bmov HCNT, ALLONES, 3; } else if ((ahc->features & AHC_CMD_CHAN) != 0) { bmov STCNT, ALLONES, 3; d902 1 a902 3 mvi STCNT[0], 0xFF; mvi STCNT[1], 0xFF; mvi STCNT[2], 0xFF; d904 4 a908 2 /* If we are the last SG block, tell the hardware. */ cmp SG_COUNT,0x01 jne data_phase_wideodd; d910 182 a1091 1 or SG_CACHEPTR, LAST_SEG; d1093 4 a1096 3 if ((ahc->flags & AHC_TARGETMODE) != 0) { test SSTAT0, TARGET jz . + 2; test DMAPARAMS, DIRECTION jz data_phase_wideodd; d1098 8 d1107 2 a1108 4 } data_phase_wideodd: if ((ahc->features & AHC_ULTRA2) != 0) { mov SINDEX, ALLONES; d1110 22 a1131 7 test SSTAT0, SDONE jnz .;/* Wait for preload to complete */ data_phase_dma_loop: test SSTAT0, SDONE jnz data_phase_dma_done; test SSTAT1,PHASEMIS jz data_phase_dma_loop; /* ie. underrun */ } else { mov DMAPARAMS call dma; } d1133 20 a1152 3 data_phase_dma_done: /* Go tell the host about any overruns */ test SXFRCTL1,BITBUCKET jnz data_phase_overrun; d1154 31 a1184 19 /* See if we completed this segment */ test STCNT[0], 0xff jnz data_phase_finish; test STCNT[1], 0xff jnz data_phase_finish; test STCNT[2], 0xff jnz data_phase_finish; /* * Advance the scatter-gather pointers if needed */ sg_advance: dec SG_COUNT; /* one less segment to go */ test SG_COUNT, 0xff jz data_phase_finish; /* Are we done? */ /* * Load a struct scatter and set up the data address and length. * If the working value of the SG count is nonzero, then * we need to load a new set of values. * * This, like all DMA's, assumes little-endian host data storage. */ a1185 1 if ((ahc->features & AHC_CMD_CHAN) != 0) { d1187 4 a1190 1 * Do we have any prefetch left??? d1192 22 a1213 1 cmp CCSGADDR, CCSGADDR_MAX jne prefetched_segs_avail; d1215 1 a1215 19 /* * Fetch MIN(CCSGADDR_MAX, (SG_COUNT * 8)) bytes. */ add A, -(CCSGRAM_MAXSEGS + 1), SG_COUNT; mvi A, CCSGADDR_MAX; jc . + 2; shl A, 3, SG_COUNT; mov CCHCNT, A; bmov CCHADDR, SG_NEXT, 4; mvi CCSGCTL, CCSGEN|CCSGRESET; test CCSGCTL, CCSGDONE jz .; and CCSGCTL, ~CCSGEN; test CCSGCTL, CCSGEN jnz .; mvi CCSGCTL, CCSGRESET; prefetched_segs_avail: bmov HADDR, CCSGRAM, 8; } else { mvi DINDEX, HADDR; mvi SG_NEXT call bcopy_4; d1217 1 a1217 3 mvi HCNT[0],SG_SIZEOF; clr HCNT[1]; clr HCNT[2]; d1219 4 a1222 1 or DFCNTRL, HDMAEN|DIRECTION|FIFORESET; d1224 2 a1225 1 call dma_finish; d1227 12 a1238 10 /* * Copy data from FIFO into SCB data pointer and data count. * This assumes that the SG segments are of the form: * struct ahc_dma_seg { * u_int32_t addr; four bytes, little-endian order * u_int32_t len; four bytes, little endian order * }; */ mvi HADDR call dfdat_in_7; } d1240 2 a1241 3 /* Track odd'ness */ test HCNT[0], 0x1 jz . + 2; xor DATA_COUNT_ODD, 0x1; d1243 1 a1243 2 if ((ahc->features & AHC_ULTRA2) == 0) { /* Load STCNT as well. It is a mirror of HCNT */ d1249 26 d1277 10 a1286 7 /* Advance the SG pointer */ clr A; /* add sizeof(struct scatter) */ add SG_NEXT[0],SG_SIZEOF; adc SG_NEXT[1],A; if ((ahc->flags & AHC_TARGETMODE) != 0) { test SSTAT0, TARGET jnz data_phase_loop; a1287 2 test SSTAT1, REQINIT jz .; test SSTAT1,PHASEMIS jz data_phase_loop; d1289 9 a1297 4 /* Ensure the last seg is visable at the shaddow layer */ if ((ahc->features & AHC_ULTRA2) != 0) { mov DFCNTRL, DMAPARAMS; test SSTAT0, SDONE jnz .;/* Wait for preload to complete */ d1300 25 a1324 11 data_phase_finish: if ((ahc->features & AHC_ULTRA2) != 0) { call ultra2_dmafinish; } /* * After a DMA finishes, save the SG and STCNT residuals back into the SCB * We use STCNT instead of HCNT, since it's a reflection of how many bytes * were transferred on the SCSI (as opposed to the host) bus. */ if ((ahc->features & AHC_CMD_CHAN) != 0) { bmov SCB_RESID_DCNT, STCNT, 3; d1326 3 a1328 3 mov SCB_RESID_DCNT[0],STCNT[0]; mov SCB_RESID_DCNT[1],STCNT[1]; mov SCB_RESID_DCNT[2],STCNT[2]; d1330 6 a1335 1 mov SCB_RESID_SGCNT, SG_COUNT; d1338 2 d1343 1 a1343 1 if ((ahc->flags & AHC_TARGETMODE) != 0) { d1348 2 a1349 1 * initiator before changing phase. d1353 8 d1362 2 a1363 48 } jmp ITloop; data_phase_overrun: if ((ahc->features & AHC_ULTRA2) != 0) { call ultra2_dmafinish; or SXFRCTL0, CLRSTCNT|CLRCHN; } /* * Turn off BITBUCKET mode and notify the host */ and SXFRCTL1, ~BITBUCKET; mvi INTSTAT,DATA_OVERRUN; jmp ITloop; ultra2_dmafinish: if ((ahc->features & AHC_ULTRA2) != 0) { test DFCNTRL, DIRECTION jnz ultra2_dmafifoempty; and DFCNTRL, ~SCSIEN; test DFCNTRL, SCSIEN jnz .; ultra2_dmafifoflush: or DFCNTRL, FIFOFLUSH; /* * The FIFOEMP status bit on the Ultra2 class * of controllers seems to be a bit flaky. * It appears that if the FIFO is full and the * transfer ends with some data in the REQ/ACK * FIFO, FIFOEMP will fall temporarily * as the data is transferred to the PCI bus. * This glitch lasts for fewer than 5 clock cycles, * so we work around the problem by ensuring the * status bit stays false through a full glitch * window. */ test DFSTATUS, FIFOEMP jz ultra2_dmafifoflush; test DFSTATUS, FIFOEMP jz ultra2_dmafifoflush; test DFSTATUS, FIFOEMP jz ultra2_dmafifoflush; test DFSTATUS, FIFOEMP jz ultra2_dmafifoflush; test DFSTATUS, FIFOEMP jz ultra2_dmafifoflush; ultra2_dmafifoempty: /* Don't clobber an inprogress host data transfer */ test DFSTATUS, MREQPEND jnz ultra2_dmafifoempty; ultra2_dmahalt: and DFCNTRL, ~(SCSIEN|HDMAEN); test DFCNTRL, HDMAEN jnz .; ret; d1366 1 a1366 1 if ((ahc->flags & AHC_INITIATORMODE) != 0) { d1371 3 a1373 1 call assert; d1375 2 a1376 2 if ((ahc->features & AHC_CMD_CHAN) != 0) { mov HCNT[0], SCB_CMDLEN; d1378 23 a1400 2 if ((ahc->features & AHC_ULTRA2) == 0) { bmov STCNT, HCNT, 3; d1402 25 a1426 11 add NONE, -17, SCB_CMDLEN; jc dma_cmd_data; /* * The data fifo seems to require 4 byte alligned * transfers from the sequencer. Force this to * be the case by clearing HADDR[0] even though * we aren't going to touch host memeory. */ bmov HADDR[0], ALLZEROS, 1; if ((ahc->features & AHC_ULTRA2) != 0) { mvi DFCNTRL, (PRELOADEN|SCSIEN|DIRECTION); d1430 6 a1435 4 bmov DFDAT, SCB_CMDSTORE, 16; jmp cmd_loop; dma_cmd_data: bmov HADDR, SCB_CMDPTR, 4; d1437 4 a1440 4 mvi DINDEX, HADDR; mvi SCB_CMDPTR call bcopy_5; clr HCNT[1]; clr HCNT[2]; d1442 10 a1451 6 if ((ahc->features & AHC_ULTRA2) == 0) { if ((ahc->features & AHC_CMD_CHAN) == 0) { call set_stcnt_from_hcnt; } mvi DFCNTRL, (SCSIEN|SDMAEN|HDMAEN|DIRECTION|FIFORESET); d1453 1 a1453 1 mvi DFCNTRL, (PRELOADEN|SCSIEN|HDMAEN|DIRECTION); d1455 1 a1455 3 cmd_loop: test SSTAT0, SDONE jnz . + 2; test SSTAT1, PHASEMIS jz cmd_loop; d1457 2 a1458 2 * Wait for our ACK to go-away on it's own * instead of being killed by SCSIEN getting cleared. d1460 2 a1461 1 test SCSISIGI, ACKI jnz .; d1471 4 a1474 3 call assert; mov SCB_TARGET_STATUS, SCSIDATL; d1491 1 a1491 1 * BDR message in response to a bad reselection would leave us pointed to d1502 6 a1507 1 or SCSISIGO,ATNO,LASTPHASE;/* turn on ATN for the retry */ a1511 25 mov FUNCTION1, SCB_TCL; mov A, FUNCTION1; if ((ahc->features & AHC_HS_MAILBOX) != 0) { /* * Work around a pausing bug in at least the aic7890. * If the host needs to update the TARGET_MSG_REQUEST * bit field, it will set the HS_MAILBOX to 1. In * response, we pause with a specific interrupt code * asking for the mask to be updated before we continue. * Ugh. */ test HS_MAILBOX, 0xF0 jz . + 2; mvi INTSTAT, UPDATE_TMSG_REQ; nop; } mov SINDEX, TARGET_MSG_REQUEST[0]; if ((ahc->features & AHC_TWIN) != 0) { /* Second Channel uses high byte bits */ test SCB_TCL, SELBUSB jz . + 2; mov SINDEX, TARGET_MSG_REQUEST[1]; } else if ((ahc->features & AHC_WIDE) != 0) { test SCB_TCL, 0x80 jz . + 2; /* target > 7 */ mov SINDEX, TARGET_MSG_REQUEST[1]; } test SINDEX, A jnz host_message_loop; d1513 3 a1515 4 and SINDEX,LID,SCB_TCL; /* lun */ and A,DISCENB,SCB_CONTROL; /* mask off disconnect privledge */ or SINDEX,A; /* or in disconnect privledge */ or SINDEX,MSG_IDENTIFYFLAG; d1565 1 d1578 1 a1578 1 mvi INTSTAT, HOST_MSG_LOOP; d1583 17 a1603 2 mesgin_complete: d1605 1 a1605 1 * We got a "command complete" message, so put the SCB_TAG into the QOUTFIFO, d1612 3 a1614 8 * sense, it will fill the kernel SCB with a request sense command and set * RETURN_1 to SEND_SENSE. If RETURN_1 is set to SEND_SENSE we redownload * the SCB, and process it as the next command by adding it to the waiting list. * If the kernel driver does not wish to request sense, it need only clear * RETURN_1, and the command is allowed to complete normally. We don't bother * to post to the QOUTFIFO in the error cases since it would require extra * work in the kernel driver to ensure that the entry was removed before the * command complete code tried processing it. d1616 25 d1642 22 a1663 5 /* * First check for residuals */ test SCB_RESID_SGCNT,0xff jnz upload_scb; test SCB_TARGET_STATUS,0xff jz complete; /* Good Status? */ d1665 1 d1668 2 a1669 4 check_status: test SCB_TARGET_STATUS,0xff jz complete; /* Just a residual? */ mvi INTSTAT,BAD_STATUS; /* let driver know */ nop; d1671 1 a1671 11 /* This SCB becomes the next to execute as it will retrieve sense */ mvi DMAPARAMS, HDMAEN|DIRECTION|FIFORESET; mov SCB_TAG call dma_scb; add_to_waiting_list: mov SCB_NEXT,WAITING_SCBH; mov WAITING_SCBH, SCBPTR; /* * Prepare our selection hardware before the busfree so we have a * high probability of winning arbitration. */ call start_selection; a1672 1 a1673 8 /* If we are untagged, clear our address up in host ram */ test SCB_CONTROL, TAG_ENB jnz complete_queue; mov A, SAVED_TCL; /* fvdl - let ahc_intr clear this to avoid race conditions */ /* mvi UNTAGGEDSCB_OFFSET call post_byte_setup; */ /* mvi SCB_LIST_NULL call post_byte; */ complete_queue: d1694 1 a1694 1 if ((ahc->flags & AHC_INITIATORMODE) != 0) { d1697 3 a1699 1 * and await the bus going free. d1702 11 d1714 7 a1720 1 call add_scb_to_disc_list; d1729 3 d1734 21 a1754 1 test SEQ_FLAGS, DPHASE jz mesgin_done; d1757 2 a1758 2 * The SCB SGPTR becomes the next one we'll download, * and the SCB DATAPTR becomes the current SHADDR. a1762 1 bmov SCB_SGCOUNT, SG_COUNT, 5; d1764 4 a1767 1 bmov SCB_DATACNT, SCB_RESID_DCNT, 3; a1768 3 mvi DINDEX, SCB_SGCOUNT; mvi SG_COUNT call bcopy_5; d1770 3 a1772 2 mvi SHADDR call bcopy_4; mvi SCB_RESID_DCNT call bcopy_3; d1774 1 a1774 1 jmp mesgin_done; d1780 2 a1781 1 * code do the rest. d1789 1 d1793 14 d1812 22 a1833 2 if ((ahc->features & AHC_WIDE) != 0) { and A,0x0f; /* lun in lower four bits */ d1835 11 a1845 1 and A,0x07; /* lun in lower three bits */ d1847 1 a1847 4 or SAVED_TCL,A; /* SAVED_TCL should be complete now */ mvi ARG_2, SCB_LIST_NULL; /* SCBID of prev SCB in disc List */ call get_untagged_SCBID; d1850 23 a1872 1 test SEQ_FLAGS, SCBPTR_VALID jz use_retrieveSCB; d1874 1 a1874 8 /* * If the SCB was found in the disconnected list (as is * always the case in non-paging scenarios), SCBPTR is already * set to the correct SCB. So, simply setup the SCB and get * on with things. */ call rem_scb_from_disc_list; jmp setup_SCB; d1878 4 a1881 4 * SCB. With SCB paging, this requires using search for both tagged * and non-tagged transactions since the SCB may exist in any slot. * If we're not using SCB paging, we can use the tag as the direct * index to the SCB. d1884 3 a1887 1 snoop_tag_loop: d1889 3 d1893 3 d1898 7 a1904 1 mvi ARG_1 call inb_next; /* tag value */ d1906 4 a1909 6 /* * Ensure that the SCB the tag points to is for * an SCB transaction to the reconnecting target. */ use_retrieveSCB: call retrieveSCB; d1911 15 a1925 2 mov A, SAVED_TCL; cmp SCB_TCL, A jne not_found_cleanup_scb; d1928 11 a1938 1 or SEQ_FLAGS,IDENTIFY_SEEN; /* make note of IDENTIFY */ a1941 1 and SCB_CONTROL, ~MK_MESSAGE; d1946 3 a1948 4 test SCB_CONTROL, DISCONNECTED jz . + 3; call add_scb_to_disc_list; jmp not_found; call add_scb_to_free_list; d1950 1 a1950 1 mvi INTSTAT, NO_MATCH; a1952 8 /* * [ ADD MORE MESSAGE HANDLING HERE ] */ /* * Locking the driver out, build a one-byte message passed in SINDEX * if there is no active message already. SINDEX is returned intact. */ d1954 5 a1958 1 or SCSISIGO,ATNO,LASTPHASE;/* turn on ATNO */ d1977 1 a1977 1 mvi INTSTAT, PERR_DETECTED; d1999 1 a1999 1 if ((ahc->flags & AHC_TARGETMODE) != 0) { d2016 1 a2016 1 * we must waitat least a data release delay plus a2038 1 mesgin_phasemis: d2040 5 a2044 70 * We expected to receive another byte, but the target changed phase */ mvi INTSTAT, MSGIN_PHASEMIS; jmp ITloop; /* * DMA data transfer. HADDR and HCNT must be loaded first, and * SINDEX should contain the value to load DFCNTRL with - 0x3d for * host->scsi, or 0x39 for scsi->host. The SCSI channel is cleared * during initialization. */ dma: mov DFCNTRL,SINDEX; dma_loop: test SSTAT0,DMADONE jnz dma_dmadone; test SSTAT1,PHASEMIS jz dma_loop; /* ie. underrun */ dma_phasemis: /* * We will be "done" DMAing when the transfer count goes to zero, or * the target changes the phase (in light of this, it makes sense that * the DMA circuitry doesn't ACK when PHASEMIS is active). If we are * doing a SCSI->Host transfer, the data FIFO should be flushed auto- * magically on STCNT=0 or a phase change, so just wait for FIFO empty * status. */ dma_checkfifo: test DFCNTRL,DIRECTION jnz dma_fifoempty; dma_fifoflush: test DFSTATUS,FIFOEMP jz dma_fifoflush; dma_fifoempty: /* Don't clobber an inprogress host data transfer */ test DFSTATUS, MREQPEND jnz dma_fifoempty; /* * Now shut the DMA enables off and make sure that the DMA enables are * actually off first lest we get an ILLSADDR. */ dma_dmadone: and DFCNTRL, ~(SCSIEN|SDMAEN|HDMAEN); dma_halt: /* * Some revisions of the aic7880 have a problem where, if the * data fifo is full, but the PCI input latch is not empty, * HDMAEN cannot be cleared. The fix used here is to attempt * to drain the data fifo until there is space for the input * latch to drain and HDMAEN de-asserts. */ if ((ahc->features & AHC_ULTRA2) == 0) { mov NONE, DFDAT; } test DFCNTRL, (SCSIEN|SDMAEN|HDMAEN) jnz dma_halt; return: ret; /* * Assert that if we've been reselected, then we've seen an IDENTIFY * message. */ assert: test SEQ_FLAGS,IDENTIFY_SEEN jnz return; /* seen IDENTIFY? */ mvi INTSTAT,NO_IDENT ret; /* no - tell the kernel */ /* * Locate a disconnected SCB either by SAVED_TCL (ARG_1 is SCB_LIST_NULL) * or by the SCBID ARG_1. The search begins at the SCB index passed in * via SINDEX which is an SCB that must be on the disconnected list. If * the SCB cannot be found, SINDEX will be SCB_LIST_NULL, otherwise, SCBPTR * is set to the proper SCB. d2046 2 d2049 5 a2053 7 mov SCBPTR,SINDEX; /* Initialize SCBPTR */ cmp ARG_1, SCB_LIST_NULL jne findSCB_by_SCBID; mov A, SAVED_TCL; mvi SCB_TCL jmp findSCB_loop; /* &SCB_TCL -> SINDEX */ findSCB_by_SCBID: mov A, ARG_1; /* Tag passed in ARG_1 */ mvi SCB_TAG jmp findSCB_loop; /* &SCB_TAG -> SINDEX */ d2055 1 a2056 1 cmp SCB_NEXT, SCB_LIST_NULL je notFound; a2057 1 dec SINDEX; /* Last comparison moved us too far */ d2059 1 a2059 23 cmp SINDIR, A jne findSCB_next; mov SINDEX, SCBPTR ret; notFound: mvi SINDEX, SCB_LIST_NULL ret; /* * Retrieve an SCB by SCBID first searching the disconnected list falling * back to DMA'ing the SCB down from the host. This routine assumes that * ARG_1 is the SCBID of interrest and that SINDEX is the position in the * disconnected list to start the search from. If SINDEX is SCB_LIST_NULL, * we go directly to the host for the SCB. */ retrieveSCB: test SEQ_FLAGS, SCBPTR_VALID jz retrieve_from_host; mov SCBPTR call findSCB; /* Continue the search */ cmp SINDEX, SCB_LIST_NULL je retrieve_from_host; /* * This routine expects SINDEX to contain the index of the SCB to be * removed, SCBPTR to be pointing to that SCB, and ARG_2 to be the * SCBID of the SCB just previous to this one in the list or SCB_LIST_NULL * if it is at the head. */ a2060 1 /* Remove this SCB from the disconnection list */ d2063 1 d2069 7 a2075 7 retrieve_from_host: /* * We didn't find it. Pull an SCB and DMA down the one we want. * We should never get here in the non-paging case. */ mov ALLZEROS call get_free_or_disc_scb; a2076 1 /* Jump instead of call as we want to return anyway */ d2078 1 a2078 44 /* * Determine whether a target is using tagged or non-tagged transactions * by first looking for a matching transaction based on the TCL and if * that fails, looking up this device in the host's untagged SCB array. * The TCL to search for is assumed to be in SAVED_TCL. The value is * returned in ARG_1 (SCB_LIST_NULL for tagged, SCBID for non-tagged). * The SCBPTR_VALID bit is set in SEQ_FLAGS if we found the information * in an SCB instead of having to go to the host. */ get_untagged_SCBID: cmp DISCONNECTED_SCBH, SCB_LIST_NULL je get_SCBID_from_host; mvi ARG_1, SCB_LIST_NULL; mov DISCONNECTED_SCBH call findSCB; cmp SINDEX, SCB_LIST_NULL je get_SCBID_from_host; or SEQ_FLAGS, SCBPTR_VALID;/* Was in disconnected list */ test SCB_CONTROL, TAG_ENB jnz . + 2; mov ARG_1, SCB_TAG ret; mvi ARG_1, SCB_LIST_NULL ret; /* * Fetch a byte from host memory given an index of (A + (256 * SINDEX)) * and a base address of SCBID_ADDR. The byte is returned in RETURN_2. */ fetch_byte: mov ARG_2, SINDEX; if ((ahc->features & AHC_CMD_CHAN) != 0) { mvi DINDEX, CCHADDR; mvi SCBID_ADDR call set_1byte_addr; mvi CCHCNT, 1; mvi CCSGCTL, CCSGEN|CCSGRESET; test CCSGCTL, CCSGDONE jz .; mvi CCSGCTL, CCSGRESET; bmov RETURN_2, CCSGRAM, 1 ret; } else { mvi DINDEX, HADDR; mvi SCBID_ADDR call set_1byte_addr; mvi HCNT[0], 1; clr HCNT[1]; clr HCNT[2]; mvi DFCNTRL, HDMAEN|DIRECTION|FIFORESET; call dma_finish; mov RETURN_2, DFDAT ret; } d2082 1 a2082 1 * index of (A + (256 * SINDEX)) and a base address of SCBID_ADDR. d2088 1 a2088 1 mvi SCBID_ADDR call set_1byte_addr; d2093 2 a2094 4 mvi SCBID_ADDR call set_1byte_addr; mvi HCNT[0], 1; clr HCNT[1]; clr HCNT[2]; a2109 5 get_SCBID_from_host: mov A, SAVED_TCL; mvi UNTAGGEDSCB_OFFSET call fetch_byte; mov RETURN_1, RETURN_2 ret; d2111 1 a2111 1 mvi INTSTAT, PERR_DETECTED; d2121 3 a2123 1 and SCSISIGO, PHASE_MASK, SCSISIGI; d2127 6 d2138 2 d2153 1 a2153 1 if ((ahc->flags & AHC_TARGETMODE) != 0) { d2179 1 a2179 1 * Setup addr assuming that A + (ARG_1 * 256) is an d2194 1 a2194 1 * Either post or fetch and SCB from host memory based on the d2204 5 a2208 1 mvi CCHCNT, SCB_64BYTE_SIZE; d2213 2 a2214 2 mvi CCHCNT, SCB_32BYTE_SIZE; if ((ahc->chip & AHC_CHIPID_MASK) == AHC_AIC7895) { d2216 1 a2216 1 bmov CCSCBRAM, SCB_CONTROL, SCB_32BYTE_SIZE; d2219 6 d2236 1 a2236 3 mvi HCNT[0], SCB_32BYTE_SIZE; clr HCNT[1]; clr HCNT[2]; d2241 2 a2242 2 mvi SINDEX, SCB_CONTROL; add A, SCB_32BYTE_SIZE, SINDEX; d2244 1 a2244 7 mov DFDAT,SINDIR; mov DFDAT,SINDIR; mov DFDAT,SINDIR; mov DFDAT,SINDIR; mov DFDAT,SINDIR; mov DFDAT,SINDIR; mov DFDAT,SINDIR; d2247 1 d2249 54 a2302 7 call dma_finish; /* If we were putting the SCB, we are done */ test DMAPARAMS, DIRECTION jz return; mvi SCB_CONTROL call dfdat_in_7; call dfdat_in_7_continued; call dfdat_in_7_continued; jmp dfdat_in_7_continued; a2303 2 mov DINDEX,SINDEX; dfdat_in_7_continued: d2309 1 d2314 12 d2333 1 d2339 10 d2351 1 d2355 1 d2360 7 d2369 1 d2373 1 d2377 2 a2378 1 dma_up_scb: d2380 2 a2381 3 mov SCB_TAG call dma_scb; unlink_disc_scb: mov DISCONNECTED_SCBH, SCB_NEXT ret; d2385 1 a2385 1 } d2393 1 d2396 7 @ 1.12 log @`response', not `responce' @ text @d1 1 a1 1 /* $NetBSD: aic7xxx.seq,v 1.11 2000/03/15 02:09:11 fvdl Exp $ */ d517 1 a517 1 /* Wait for preceeding I/O session to complete. */ @ 1.12.2.1 log @Merge Aug 24 -current into the kqueue branch. @ text @d1 1 a1 1 /* $NetBSD: aic7xxx.seq,v 1.13 2001/08/20 12:00:52 wiz Exp $ */ d517 1 a517 1 /* Wait for preceding I/O session to complete. */ @ 1.11 log @New ahc driver, a port of Justin Gibbs' FreeBSD driver. This adds support for the U2W chips, and U160 controllers. @ text @d1 1 a1 1 /* $NetBSD$ */ d1064 1 a1064 1 * BDR message in responce to a bad reselection would leave us pointed to @ 1.11.6.1 log @Catch up to -current. @ text @d1 1 a1 1 /* $NetBSD: aic7xxx.seq,v 1.11 2000/03/15 02:09:11 fvdl Exp $ */ d1064 1 a1064 1 * BDR message in response to a bad reselection would leave us pointed to @ 1.11.6.2 log @Catch up with -current. @ text @d1 1 a1 1 /* $NetBSD: aic7xxx.seq,v 1.11.6.1 2001/06/21 20:04:10 nathanw Exp $ */ d517 1 a517 1 /* Wait for preceding I/O session to complete. */ @ 1.10 log @port-i386/5706: I had no problem with the previous patch, but someone else has. Rolling it back while we try and figure it out. @ text @d1 1 a1 1 /* $NetBSD: aic7xxx.seq,v 1.9 1998/07/03 23:45:10 mjacob Exp $ */ d3 2 a4 2 /*+M*********************************************************************** *Adaptec 274x/284x/294x device driver for Linux and FreeBSD. d6 2 a7 3 *Copyright (c) 1994 John Aycock * The University of Calgary Department of Computer Science. * All rights reserved. d9 8 a16 3 *FreeBSD, Twin, Wide, 2 command per target support, tagged queuing, *SCB paging and other optimizations: *Copyright (c) 1994, 1995, 1996 Justin Gibbs. All rights reserved. d18 2 a19 15 *Redistribution and use in source and binary forms, with or without *modification, are permitted provided that the following conditions *are met: *1. Redistributions of source code must retain the above copyright * notice, this list of conditions, and the following disclaimer. *2. Redistributions 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. *3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of Calgary * Department of Computer Science and its contributors. *4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. d21 11 a31 11 *THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND *ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE *IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE *ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE *FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL *DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS *OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) *HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT *LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY *OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF *SUCH DAMAGE. d33 2 a34 13 * from Id: aic7xxx.seq,v 1.42 1996/06/09 17:29:11 gibbs Exp * *-M************************************************************************/ VERSION AIC7XXX_SEQ_VER "$NetBSD: aic7xxx.seq,v 1.8 1998/04/16 07:07:37 leo Exp $" #if defined(__NetBSD__) #include "../../ic/aic7xxxreg.h" #include "../../scsipi/scsi_message.h" #elif defined(__FreeBSD__) #include "../../dev/aic7xxx/aic7xxx_reg.h" #include "../../scsi/scsi_message.h" #endif d37 1 a37 4 * We can't just use ACCUM in the sequencer code because it * must be treated specially by the assembler, and it currently * looks for the symbol 'A'. This is the only register defined in * the assembler's symbol space. d39 10 a48 1 A = ACCUM d50 3 a52 1 /* After starting the selection hardware, we check for reconnecting targets d61 5 a65 4 * of SCBs that are awaiting selection. Since 0-0xfe are valid SCB offsets, * SCB_LIST_NULL is 0xff which is out of range. The kernel driver must * add an entry to this list everytime a request sense occurs. The sequencer * will automatically consume the entries. a67 4 /* * We assume that the kernel driver may reset us at any time, even in the * middle of a DMA, so clear DFCNTRL too. */ d69 11 a79 13 clr DFCNTRL clr SCSISIGO /* De-assert BSY */ /* * We jump to start after every bus free. */ start: and FLAGS,0x0f /* clear target specific flags */ mvi SCSISEQ,ENRSELI /* Always allow reselection */ clr SCSIRATE /* * We don't know the target we will * connect to, so default to narrow * transfers to avoid parity problems. */ d81 36 a116 15 /* * Are we a twin channel device? * For fairness, we check the other bus first, * since we just finished a transaction on the * current channel. */ test FLAGS,TWIN_BUS jz start2 xor SBLKCTL,SELBUSB /* Toggle to the other bus */ test SSTAT0,SELDI jnz reselect xor SBLKCTL,SELBUSB /* Toggle to the original bus */ start2: test SSTAT0,SELDI jnz reselect cmp WAITING_SCBH,SCB_LIST_NULL jne start_waiting mov A, QCNTMASK test QINCNT,A jz poll_for_work d120 37 a156 51 * SCBs in the list of SCBs awaiting selection. Set the SCB * pointer from the FIFO so we see the right bank of SCB * registers. */ mov SCBPTR,QINFIFO /* * See if there is not already an active SCB for this target. This code * locks out on a per target basis instead of target/lun. Although this * is not ideal for devices that have multiple luns active at the same * time, it is faster than looping through all SCB's looking for active * commands. It may be benificial to make findscb a more general procedure * to see if the added cost of the search is negligible. This code also * assumes that the kernel driver will clear the active flags on board * initialization, board reset, and a target SELTO. Tagged commands * don't set the active bits since you can queue more than one command * at a time. We do, however, look to see if there are any non-tagged * I/Os in progress, and requeue the command if there are. Tagged and * non-tagged commands cannot be mixed to a single target. */ test_busy: mov FUNCTION1,SCB_TCL mov A,FUNCTION1 test SCB_TCL,0x88 jz test_a /* Id < 8 && A channel */ test ACTIVE_B,A jnz requeue test SCB_CONTROL,TAG_ENB jnz start_scb /* Mark the current target as busy */ or ACTIVE_B,A jmp start_scb /* Place the currently active SCB back on the queue for later processing */ requeue: mov QINFIFO, SCBPTR jmp poll_for_work /* * Pull the first entry off of the waiting for selection list * We don't have to "test_busy" because only transactions that * have passed that test can be in the waiting_scb list. */ start_waiting: mov SCBPTR,WAITING_SCBH jmp start_scb2 test_a: test ACTIVE_A,A jnz requeue test SCB_CONTROL,TAG_ENB jnz start_scb /* Mark the current target as busy */ or ACTIVE_A,A d159 13 a171 8 mov SCB_NEXT,WAITING_SCBH mov WAITING_SCBH, SCBPTR start_scb2: and SINDEX,0xf7,SBLKCTL /* Clear the channel select bit */ and A,0x08,SCB_TCL /* Get new channel bit */ or SINDEX,A mov SBLKCTL,SINDEX /* select channel */ mov SCB_TCL call initialize_scsiid a172 6 /* * Enable selection phase as an initiator, and do automatic ATN * after the selection. We do this now so that we can overlap the * rest of our work to set up this target with the arbitration and * selection bus phases. */ d174 43 a216 2 mvi SCSISEQ,0x58 /* ENSELO|ENAUTOATNO|ENRSELI */ d218 1 a218 6 * As soon as we get a successful selection, the target should go * into the message out phase since we have ATN asserted. Prepare * the message to send. * * Messages are stored in scratch RAM starting with a length byte * followed by the message itself. d220 188 d409 1 a409 33 mk_identify: and A,DISCENB,SCB_CONTROL /* mask off disconnect privledge */ and MSG0,0x7,SCB_TCL /* lun */ or MSG0,A /* or in disconnect privledge */ or MSG0,MSG_IDENTIFYFLAG mvi MSG_LEN, 1 /* * Send a tag message if TAG_ENB is set in the SCB control block. * Use SCB_TAG (the position in the kernel's SCB array) as the tag value. */ mk_tag: test SCB_CONTROL,TAG_ENB jz mk_message mvi DINDEX, MSG1 and DINDIR,0x23,SCB_CONTROL mov DINDIR,SCB_TAG add MSG_LEN,COMP_MSG0,DINDEX /* update message length */ /* * Interrupt the driver, and allow it to tweak the message buffer * if it asks. */ mk_message: test SCB_CONTROL,MK_MESSAGE jz wait_for_selection mvi INTSTAT,AWAITING_MSG wait_for_selection: test SSTAT0,SELDO jnz select test SSTAT0,SELDI jz wait_for_selection d412 1 a412 2 * reselected, but haven't seen an IDENTIFY message from the target * yet. d414 17 a430 5 reselect: clr MSG_LEN /* Don't have anything in the mesg buffer */ mov SELID call initialize_scsiid or FLAGS,RESELECTED jmp select2 d433 1 a433 1 * After the selection, remove this SCB from the "waiting for selection" d438 196 a633 15 select: mov WAITING_SCBH,SCB_NEXT or FLAGS,SELECTED select2: /* * Set CLRCHN here before the target has entered a data transfer mode - * with synchronous SCSI, if you do it later, you blow away some * data in the SCSI FIFO that the target has already sent to you. */ or SXFRCTL0,CLRCHN /* * Initialize SCSIRATE with the appropriate value for this target. */ call ndx_dtr mov SCSIRATE,SINDIR d635 5 a639 30 /* * Initialize Ultra mode setting. */ mov FUNCTION1,SCSIID mov A,FUNCTION1 and SINDEX,0xdf,SXFRCTL0 /* default to Ultra disabled */ test SCSIID, 0x80 jnz ultra_b /* Target ID > 7 */ test SBLKCTL, SELBUSB jnz ultra_b /* Second channel device */ test ULTRA_ENB,A jz set_sxfrctl0 or SINDEX, ULTRAEN jmp set_sxfrctl0 ultra_b: test ULTRA_ENB_B,A jz set_sxfrctl0 or SINDEX, ULTRAEN set_sxfrctl0: mov SXFRCTL0,SINDEX mvi SCSISEQ,ENAUTOATNP /* * ATN on parity errors * for "in" phases */ mvi CLRSINT1,CLRBUSFREE mvi CLRSINT0,0x60 /* CLRSELDI|CLRSELDO */ /* * Main loop for information transfer phases. If BSY is false, then * we have a bus free condition, expected or not. Otherwise, wait * for the target to assert REQ before checking MSG, C/D and I/O * for the bus phase. * */ d641 29 a669 2 test SSTAT1,BUSFREE jnz p_busfree test SSTAT1,REQINIT jz ITloop d671 14 a684 20 and A,PHASE_MASK,SCSISIGI mov LASTPHASE,A mov SCSISIGO,A cmp ALLZEROS,A je p_dataout cmp A,P_DATAIN je p_datain cmp A,P_COMMAND je p_command cmp A,P_MESGOUT je p_mesgout cmp A,P_STATUS je p_status cmp A,P_MESGIN je p_mesgin mvi INTSTAT,BAD_PHASE /* unknown phase - signal driver */ jmp ITloop /* Try reading the bus again. */ p_dataout: mvi DMAPARAMS,0x7d /* * WIDEODD|SCSIEN|SDMAEN|HDMAEN| * DIRECTION|FIFORESET */ jmp data_phase_init d691 28 a718 9 mov STCNT0,SCB_RESID_DCNT0 mov STCNT1,SCB_RESID_DCNT1 mov STCNT2,SCB_RESID_DCNT2 jmp data_phase_loop p_datain: mvi DMAPARAMS,0x79 /* * WIDEODD|SCSIEN|SDMAEN|HDMAEN| * !DIRECTION|FIFORESET d720 4 a723 2 data_phase_init: call assert d725 31 a755 3 test FLAGS, DPHASE jnz data_phase_reinit call sg_scb2ram or FLAGS, DPHASE /* We have seen a data phase */ d759 1 a759 1 test SG_COUNT, 0xff jnz data_phase_inbounds d766 11 a776 5 or SXFRCTL1,BITBUCKET mvi STCNT0,0xff mvi STCNT1,0xff mvi STCNT2,0xff d778 11 a788 3 /* If we are the last SG block, don't set wideodd. */ cmp SG_COUNT,0x01 jne data_phase_wideodd and DMAPARAMS, 0xbf /* Turn off WIDEODD */ d790 10 a799 1 mov DMAPARAMS call dma d801 1 d803 1 a803 1 test SXFRCTL1,BITBUCKET jnz data_phase_overrun d805 4 a808 2 /* Exit if we had an underrun */ test SSTAT0,SDONE jz data_phase_finish /* underrun STCNT != 0 */ d814 1 a814 7 dec SG_COUNT /* one less segment to go */ test SG_COUNT, 0xff jz data_phase_finish /* Are we done? */ clr A /* add sizeof(struct scatter) */ add SG_NEXT0,SG_SIZEOF,SG_NEXT0 adc SG_NEXT1,A,SG_NEXT1 d816 1 d825 74 a898 65 clr HCNT2 clr HCNT1 mvi HCNT0,SG_SIZEOF mov HADDR0,SG_NEXT0 mov HADDR1,SG_NEXT1 mov HADDR2,SG_NEXT2 mov HADDR3,SG_NEXT3 or DFCNTRL,0xd /* HDMAEN|DIRECTION|FIFORESET */ /* * Wait for DMA from host memory to data FIFO to complete, then disable * DMA and wait for it to acknowledge that it's off. */ dma_finish: test DFSTATUS,HDONE jz dma_finish /* Turn off DMA preserving WIDEODD */ and DFCNTRL,WIDEODD dma_finish2: test DFCNTRL,HDMAENACK jnz dma_finish2 /* * Copy data from FIFO into SCB data pointer and data count. This assumes * that the struct scatterlist has this structure (this and sizeof(struct * scatterlist) == 12 are asserted in aic7xxx.c for the Linux driver): * * struct scatterlist { * char *address; four bytes, little-endian order * ... four bytes, ignored * unsigned short length; two bytes, little-endian order * } * * * In FreeBSD, the scatter list entry is only 8 bytes. * * struct ahc_dma_seg { * physaddr addr; four bytes, little-endian order * long len; four bytes, little endian order * }; */ mov HADDR0,DFDAT mov HADDR1,DFDAT mov HADDR2,DFDAT mov HADDR3,DFDAT /* * For Linux, we must throw away four bytes since there is a 32bit gap * in the middle of a struct scatterlist. */ #ifdef __linux__ mov NONE,DFDAT mov NONE,DFDAT mov NONE,DFDAT mov NONE,DFDAT #endif mov HCNT0,DFDAT mov HCNT1,DFDAT mov HCNT2,DFDAT /* Load STCNT as well. It is a mirror of HCNT */ mov STCNT0,HCNT0 mov STCNT1,HCNT1 mov STCNT2,HCNT2 test SSTAT1,PHASEMIS jz data_phase_loop d901 3 d909 25 a933 5 mov SCB_RESID_DCNT0,STCNT0 mov SCB_RESID_DCNT1,STCNT1 mov SCB_RESID_DCNT2,STCNT2 mov SCB_RESID_SGCNT, SG_COUNT jmp ITloop d936 4 d943 38 a980 3 and SXFRCTL1,0x7f /* ~BITBUCKET */ mvi INTSTAT,DATA_OVERRUN jmp ITloop d982 1 d987 1 a987 1 call assert d989 50 a1038 18 /* * Load HADDR and HCNT. */ mov HADDR0, SCB_CMDPTR0 mov HADDR1, SCB_CMDPTR1 mov HADDR2, SCB_CMDPTR2 mov HADDR3, SCB_CMDPTR3 mov HCNT0, SCB_CMDLEN clr HCNT1 clr HCNT2 mov STCNT0, HCNT0 mov STCNT1, HCNT1 mov STCNT2, HCNT2 mvi 0x3d call dma # SCSIEN|SDMAEN|HDMAEN| # DIRECTION|FIFORESET jmp ITloop d1045 4 a1048 2 mvi SCB_TARGET_STATUS call inb_first jmp mesgin_done d1051 22 a1072 2 * Message out phase. If there is not an active message, but the target * took us into this phase anyway, build a no-op message and send it. d1074 2 d1077 33 a1109 4 test MSG_LEN, 0xff jnz p_mesgout_start mvi MSG_NOOP call mk_mesg /* build NOP message */ p_mesgout_start: d1111 2 a1112 12 * Set up automatic PIO transfer from MSG0. Bit 3 in * SXFRCTL0 (SPIOEN) is already on. */ mvi SINDEX,MSG0 mov DINDEX,MSG_LEN /* * When target asks for a byte, drop ATN if it's the last one in * the message. Otherwise, keep going until the message is exhausted. * * Keep an eye out for a phase change, in case the target issues * a MESSAGE REJECT. d1114 20 a1133 13 p_mesgout_loop: test SSTAT1,PHASEMIS jnz p_mesgout_phasemis test SSTAT0,SPIORDY jz p_mesgout_loop test SSTAT1,PHASEMIS jnz p_mesgout_phasemis cmp DINDEX,1 jne p_mesgout_outb /* last byte? */ mvi CLRSINT1,CLRATNO /* drop ATN */ p_mesgout_outb: dec DINDEX or CLRSINT0, CLRSPIORDY mov SCSIDATL,SINDIR p_mesgout4: test DINDEX,0xff jnz p_mesgout_loop d1136 1 a1136 1 * If the next bus phase after ATN drops is a message out, it means d1139 2 a1140 3 p_mesgout_snoop: test SSTAT1,BUSFREE jnz p_mesgout_done test SSTAT1,REQINIT jz p_mesgout_snoop a1141 8 test SSTAT1,PHASEMIS jnz p_mesgout_done or SCSISIGO,ATNO /* turn on ATNO */ jmp ITloop p_mesgout_phasemis: mvi CLRSINT1,CLRATNO /* Be sure to turn ATNO off */ d1143 4 a1146 2 clr MSG_LEN /* no active msg */ jmp ITloop d1152 1 a1152 2 mvi A call inb_first /* read the 1st message byte */ mov REJBYTE,A /* save it for the driver */ d1154 21 a1174 21 test A,MSG_IDENTIFYFLAG jnz mesgin_identify cmp A,MSG_DISCONNECT je mesgin_disconnect cmp A,MSG_SAVEDATAPOINTER je mesgin_sdptrs cmp ALLZEROS,A je mesgin_complete cmp A,MSG_RESTOREPOINTERS je mesgin_rdptrs cmp A,MSG_EXTENDED je mesgin_extended cmp A,MSG_MESSAGE_REJECT je mesgin_reject rej_mesgin: /* * We have no idea what this message in is, and there's no way * to pass it up to the kernel, so we issue a message reject and * hope for the best. Since we're now using manual PIO mode to * read in the message, there should no longer be a race condition * present when we assert ATN. In any case, rejection should be a * rare occurrence - signal the driver when it happens. */ or SCSISIGO,ATNO /* turn on ATNO */ mvi INTSTAT,SEND_REJECT /* let driver know */ mvi MSG_MESSAGE_REJECT call mk_mesg d1177 2 a1178 2 call inb_last /*ack & turn auto PIO back on*/ jmp ITloop d1183 15 a1197 16 * We got a "command complete" message, so put the SCB_TAG into QUEUEOUT, * and trigger a completion interrupt. Check status for non zero return * and interrupt driver if needed. This allows the driver to interpret * errors only when they occur instead of always uploading the scb. If * the status is SCSI_CHECK, the driver will download a new scb requesting * sense to replace the old one, modify the "waiting for selection" SCB list * and set RETURN_1 to SEND_SENSE. If RETURN_1 is set to SEND_SENSE the * sequencer imediately jumps to main loop where it will run down the waiting * SCB list and process the sense request. If the kernel driver does not * wish to request sense, it need only clear RETURN_1, and the command is * allowed to complete. We don't bother to post to the QOUTFIFO in the * error case since it would require extra work in the kernel driver to * ensure that the entry was removed before the command complete code tried * processing it. * * First check for residuals d1199 1 a1199 1 test SCB_RESID_SGCNT,0xff jz check_status d1201 1 a1201 6 * If we have a residual count, interrupt and tell the host. Other * alternatives are to pause the sequencer on all command completes (yuck), * dma the resid directly to the host (slick, we may have space to do it now) * or have the sequencer pause itself when it encounters a non-zero resid * (unecessary pause just to flag the command -yuck-, but takes one instruction * and since it shouldn't happen that often is good enough for our purposes). d1203 22 a1224 2 resid: mvi INTSTAT,RESIDUAL a1225 31 check_status: test SCB_TARGET_STATUS,0xff jz status_ok /* Good Status? */ mvi INTSTAT,BAD_STATUS /* let driver know */ cmp RETURN_1, SEND_SENSE jne status_ok jmp mesgin_done status_ok: /* First, mark this target as free. */ test SCB_CONTROL,TAG_ENB jnz test_immediate /* * Tagged commands * don't busy the * target. */ mov FUNCTION1,SCB_TCL mov A,FUNCTION1 test SCB_TCL,0x88 jz clear_a xor ACTIVE_B,A jmp test_immediate clear_a: xor ACTIVE_A,A test_immediate: test SCB_CMDLEN,0xff jnz complete /* Immediate message complete */ /* * Pause the sequencer until the driver gets around to handling the command * complete. This is so that any action that might require carefull timing * with the completion of this command can occur. */ mvi INTSTAT,IMMEDDONE jmp start d1227 27 a1253 40 mov QOUTFIFO,SCB_TAG mvi INTSTAT,CMDCMPLT jmp mesgin_done /* * Is it an extended message? Copy the message to our message buffer and * notify the host. The host will tell us whether to reject this message, * respond to it with the message that the host placed in our message buffer, * or simply to do nothing. */ mesgin_extended: mvi MSGIN_EXT_LEN call inb_next mvi MSGIN_EXT_OPCODE call inb_next mov A, MSGIN_EXT_LEN dec A /* Length counts the op code */ mvi SINDEX, MSGIN_EXT_BYTE0 mesgin_extended_loop: test A, 0xFF jz mesgin_extended_intr cmp SINDEX, MSGIN_EXT_LASTBYTE je mesgin_extended_dump call inb_next dec A /* * We pass the arg to inb in SINDEX, but DINDEX is the one incremented * so update SINDEX with DINDEX's value before looping again. */ mov DINDEX jmp mesgin_extended_loop mesgin_extended_dump: /* We have no more storage space, so dump the rest */ test A, 0xFF jz mesgin_extended_intr mvi NONE call inb_next dec A jmp mesgin_extended_dump mesgin_extended_intr: mvi INTSTAT,EXTENDED_MSG /* let driver know */ cmp RETURN_1,SEND_REJ je rej_mesgin cmp RETURN_1,SEND_MSG jne mesgin_done /* The kernel has setup a message to be sent */ or SCSISIGO,ATNO /* turn on ATNO */ jmp mesgin_done d1255 1 d1261 3 a1263 18 or SCB_CONTROL,DISCONNECTED test FLAGS, PAGESCBS jz mesgin_done /* * Link this SCB into the DISCONNECTED list. This list holds the * candidates for paging out an SCB if one is needed for a new command. * Modifying the disconnected list is a critical(pause dissabled) section. */ mvi SCB_PREV, SCB_LIST_NULL mvi SEQCTL,0x50 /* PAUSEDIS|FASTMODE */ mov SCB_NEXT, DISCONNECTED_SCBH mov DISCONNECTED_SCBH, SCBPTR cmp SCB_NEXT,SCB_LIST_NULL je linkdone mov SCBPTR,SCB_NEXT mov SCB_PREV,DISCONNECTED_SCBH mov SCBPTR,DISCONNECTED_SCBH linkdone: mvi SEQCTL,0x10 /* !PAUSEDIS|FASTMODE */ jmp mesgin_done d1266 5 a1270 2 * Save data pointers message? Copy working values into the SCB, * usually in preparation for a disconnect. d1273 21 a1293 2 call sg_ram2scb jmp mesgin_done d1302 2 a1303 2 and FLAGS,0xef /* * !DPHASE we'll reload them d1305 1 d1307 1 a1307 1 jmp mesgin_done d1315 21 a1335 9 test A,0x78 jnz rej_mesgin /*!DiscPriv|!LUNTAR|!Reserved*/ and A,0x07 /* lun in lower three bits */ or SAVED_TCL,A,SELID and SAVED_TCL,0xf7 and A,SELBUSB,SBLKCTL /* B Channel?? */ or SAVED_TCL,A call inb_last /* ACK */ d1338 2 a1339 2 * If we get one, we use the tag returned to switch to find the proper * SCB. With SCB paging, this requires using findSCB for both tagged d1344 2 a1345 1 mvi ARG_1,SCB_LIST_NULL /* Default to no-tag */ d1347 3 a1349 5 test SSTAT1,BUSFREE jnz use_findSCB test SSTAT1,REQINIT jz snoop_tag_loop test SSTAT1,PHASEMIS jnz use_findSCB mvi A call inb_first cmp A,MSG_SIMPLE_Q_TAG jne use_findSCB d1351 1 a1351 9 mvi ARG_1 call inb_next /* tag value */ /* * See if the tag is in range. The tag is < SCBCOUNT if we add * the complement of SCBCOUNT to the incomming tag and there is * no carry. */ mov A,COMP_SCBCOUNT add SINDEX,A,ARG_1 jc abort_tag d1353 6 a1358 8 /* * Ensure that the SCB the tag points to is for an SCB transaction * to the reconnecting target. */ test FLAGS, PAGESCBS jz index_by_tag call inb_last /* Ack Tag */ use_findSCB: mov ALLZEROS call findSCB /* Have to search */ d1360 20 a1379 26 and SCB_CONTROL,0xfb /* clear disconnect bit in SCB */ or FLAGS,IDENTIFY_SEEN /* make note of IDENTIFY */ jmp ITloop index_by_tag: mov SCBPTR,ARG_1 mov A,SAVED_TCL cmp SCB_TCL,A jne abort_tag test SCB_CONTROL,TAG_ENB jz abort_tag call inb_last /* Ack Successful tag */ jmp setup_SCB abort_tag: or SCSISIGO,ATNO /* turn on ATNO */ mvi INTSTAT,ABORT_TAG /* let driver know */ mvi MSG_ABORT_TAG call mk_mesg /* ABORT TAG message */ jmp mesgin_done /* * Message reject? Let the kernel driver handle this. If we have an * outstanding WDTR or SDTR negotiation, assume that it's a response from * the target selecting 8bit or asynchronous transfer, otherwise just ignore * it since we have no clue what it pertains to. */ mesgin_reject: mvi INTSTAT, REJECT_MSG jmp mesgin_done a1385 16 * Bus free phase. It might be useful to interrupt the device * driver if we aren't expecting this. For now, make sure that * ATN isn't being asserted and look for a new command. */ p_busfree: mvi CLRSINT1,CLRATNO clr LASTPHASE /* * if this is an immediate command, perform a psuedo command complete to * notify the driver. */ test SCB_CMDLEN,0xff jz status_ok jmp start /* d1390 2 a1391 16 mvi SEQCTL,0x50 /* PAUSEDIS|FASTMODE */ test MSG_LEN,0xff jz mk_mesg1 /* Should always succeed */ /* * Hmmm. For some reason the mesg buffer is in use. * Tell the driver. It should look at SINDEX to find * out what we wanted to use the buffer for and resolve * the conflict. */ mvi SEQCTL,0x10 /* !PAUSEDIS|FASTMODE */ mvi INTSTAT,MSG_BUFFER_BUSY mk_mesg1: mvi MSG_LEN,1 /* length = 1 */ mov MSG0,SINDEX /* 1-byte message */ mvi SEQCTL,0x10 ret /* !PAUSEDIS|FASTMODE */ d1408 3 a1410 1 d1412 1 a1412 2 or CLRSINT0, CLRSPIORDY mov NONE,SCSIDATL /*dummy read from latch to ACK*/ d1414 10 a1423 2 test SSTAT1,PHASEMIS jnz mesgin_phasemis test SSTAT0,SPIORDY jz inb_next_wait /* wait for next byte */ d1425 2 a1426 3 mov DINDEX,SINDEX test SSTAT1,PHASEMIS jnz mesgin_phasemis mov DINDIR,SCSIBUSL ret /*read byte directly from bus*/ d1428 2 a1429 1 mov NONE,SCSIDATL ret /*dummy read from latch to ACK*/ d1431 40 d1475 2 a1476 2 mvi INTSTAT, MSGIN_PHASEMIS jmp ITloop d1485 5 a1489 4 mov DFCNTRL,SINDEX dma1: test SSTAT0,DMADONE jnz dma3 test SSTAT1,PHASEMIS jz dma1 /* ie. underrun */ d1499 8 a1506 5 dma3: test SINDEX,DIRECTION jnz dma5 dma4: test DFSTATUS,FIFOEMP jz dma4 d1511 14 a1524 5 dma5: /* disable DMA, but maintain WIDEODD */ and DFCNTRL,WIDEODD dma6: test DFCNTRL,0x38 jnz dma6 /* SCSIENACK|SDMAENACK|HDMAENACK */ d1526 1 a1526 12 ret /* * Common SCSI initialization for selection and reselection. Expects * the target SCSI ID to be in the upper four bits of SINDEX, and A's * contents are stomped on return. */ initialize_scsiid: and SINDEX,0xf0 /* Get target ID */ and A,0x0f,SCSIID or SINDEX,A mov SCSIID,SINDEX ret d1533 1 a1533 2 test FLAGS,RESELECTED jz return /* reselected? */ test FLAGS,IDENTIFY_SEEN jnz return /* seen IDENTIFY? */ d1535 1 a1535 1 mvi INTSTAT,NO_IDENT ret /* no - cause a kernel panic */ d1538 5 a1542 5 * Locate the SCB matching the target ID/channel/lun in SAVED_TCL, and the tag * value in ARG_1. If ARG_1 == SCB_LIST_NULL, we're looking for a non-tagged * SCB. Have the kernel print a warning message if it can't be found, and * generate an ABORT/ABORT_TAG message to the target. SINDEX should be * cleared on call. d1545 37 a1581 13 mov A,SAVED_TCL mov SCBPTR,SINDEX /* switch to next SCB */ mvi SEQCTL,0x50 /* PAUSEDIS|FASTMODE */ cmp SCB_TCL,A jne findSCB1 /* target ID/channel/lun match? */ test SCB_CONTROL,DISCONNECTED jz findSCB1 /*should be disconnected*/ test SCB_CONTROL,TAG_ENB jnz findTaggedSCB cmp ARG_1,SCB_LIST_NULL je foundSCB jmp findSCB1 findTaggedSCB: mov A, ARG_1 /* Tag passed in ARG_1 */ cmp SCB_TAG,A jne findSCB1 /* Found it? */ foundSCB: test FLAGS,PAGESCBS jz foundSCB_ret d1583 5 a1587 12 cmp SCB_NEXT,SCB_LIST_NULL je unlink_prev mov SAVED_LINKPTR, SCB_PREV mov SCBPTR, SCB_NEXT mov SCB_PREV, SAVED_LINKPTR mov SCBPTR, SINDEX unlink_prev: cmp SCB_PREV,SCB_LIST_NULL je rHead/* At the head of the list */ mov SAVED_LINKPTR, SCB_NEXT mov SCBPTR, SCB_PREV mov SCB_NEXT, SAVED_LINKPTR mov SCBPTR, SINDEX mvi SEQCTL,0x10 ret /* !PAUSEDIS|FASTMODE */ d1589 1 a1589 42 mov DISCONNECTED_SCBH,SCB_NEXT foundSCB_ret: mvi SEQCTL,0x10 ret /* !PAUSEDIS|FASTMODE */ findSCB1: mvi SEQCTL,0x10 /* !PAUSEDIS|FASTMODE */ inc SINDEX mov A,SCBCOUNT cmp SINDEX,A jne findSCB mvi INTSTAT,NO_MATCH /* not found - signal kernel */ cmp RETURN_1,SCB_PAGEDIN je return or SCSISIGO,ATNO /* assert ATNO */ cmp ARG_1,SCB_LIST_NULL jne find_abort_tag mvi MSG_ABORT call mk_mesg jmp ITloop find_abort_tag: mvi MSG_ABORT_TAG call mk_mesg jmp ITloop /* * Make a working copy of the scatter-gather parameters from the SCB. */ sg_scb2ram: mov HADDR0, SCB_DATAPTR0 mov HADDR1, SCB_DATAPTR1 mov HADDR2, SCB_DATAPTR2 mov HADDR3, SCB_DATAPTR3 mov HCNT0, SCB_DATACNT0 mov HCNT1, SCB_DATACNT1 mov HCNT2, SCB_DATACNT2 mov STCNT0, HCNT0 mov STCNT1, HCNT1 mov STCNT2, HCNT2 mov SG_COUNT,SCB_SGCOUNT mov SG_NEXT0, SCB_SGPTR0 mov SG_NEXT1, SCB_SGPTR1 mov SG_NEXT2, SCB_SGPTR2 mov SG_NEXT3, SCB_SGPTR3 ret d1591 1 d1593 2 a1594 4 * Copying RAM values back to SCB, for Save Data Pointers message, but * only if we've actually been into a data phase to change them. This * protects against bogus data in scratch ram and the residual counts * since they are only initialized when we go into data_in or data_out. d1596 231 a1826 13 sg_ram2scb: test FLAGS, DPHASE jz return mov SCB_SGCOUNT,SG_COUNT mov SCB_SGPTR0,SG_NEXT0 mov SCB_SGPTR1,SG_NEXT1 mov SCB_SGPTR2,SG_NEXT2 mov SCB_SGPTR3,SG_NEXT3 mov SCB_DATAPTR0,SHADDR0 mov SCB_DATAPTR1,SHADDR1 mov SCB_DATAPTR2,SHADDR2 mov SCB_DATAPTR3,SHADDR3 d1829 2 a1830 1 * Use the residual number since STCNT is corrupted by any message transfer d1832 33 a1864 3 mov SCB_DATACNT0,SCB_RESID_DCNT0 mov SCB_DATACNT1,SCB_RESID_DCNT1 mov SCB_DATACNT2,SCB_RESID_DCNT2 ret d1866 1 d1868 3 a1870 3 * Add the array base TARG_SCRATCH to the target offset (the target address * is in SCSIID), and return the result in SINDEX. The accumulator * contains the 3->8 decoding of the target ID on return. d1872 2 a1873 6 ndx_dtr: shr A,SCSIID,4 test SBLKCTL,SELBUSB jz ndx_dtr_2 or A,0x08 /* Channel B entries add 8 */ ndx_dtr_2: add SINDEX,TARG_SCRATCH,A ret @ 1.10.14.1 log @Update thorpej_scsipi to -current as of a month ago A i386 GENERIC kernel compiles without the siop, ahc and bha drivers (will be updated later). i386 IDE/ATAPI and ncr work, as well as sparc/esp_sbus. alpha should work as well (untested yet). siop, ahc and bha will be updated once I've updated the branch to current -current, as well as machine-dependant code. @ text @d1 1 a1 1 /* $NetBSD: aic7xxx.seq,v 1.11 2000/03/15 02:09:11 fvdl Exp $ */ d3 6 a8 2 /* * Adaptec 274x/284x/294x device driver firmware for Linux and FreeBSD. d10 3 a12 2 * Copyright (c) 1994-2000 Justin Gibbs. * All rights reserved. d14 15 a28 8 * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions, and the following disclaimer, * without modification. * 2. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. d30 11 a40 2 * Alternatively, this software may be distributed under the terms of the * the GNU Public License ("GPL"). d42 1 a42 11 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. d44 11 a54 2 * $FreeBSD: src/sys/dev/aic7xxx/aic7xxx.seq,v 1.94 2000/02/09 21:25:00 gibbs Exp $ */ d57 4 a60 1 * #ifdef __NetBSD__ d62 1 a62 10 #include #include /* * Assembler can't handle ifdef. * * #else * #include * #include * #endif */ d64 1 a64 3 /* * A few words on the waiting SCB list: * After starting the selection hardware, we check for reconnecting targets d73 4 a76 5 * of SCBs that are awaiting selection. Since 0-0xfe are valid SCB indexes, * SCB_LIST_NULL is 0xff which is out of range. An entry is also added to * this list everytime a request sense occurs or after completing a non-tagged * command for which a second SCB has been queued. The sequencer will * automatically consume the entries. d79 4 d84 13 a96 11 clr SCSISIGO; /* De-assert BSY */ mvi MSG_OUT, MSG_NOOP; /* No message to send */ and SXFRCTL1, ~BITBUCKET; /* Always allow reselection */ and SCSISEQ, ENSELI|ENRSELI|ENAUTOATNP, SCSISEQ_TEMPLATE; if ((ahc->features & AHC_CMD_CHAN) != 0) { /* Ensure that no DMA operations are in progress */ clr CCSGCTL; clr CCSCBCTL; } d98 15 a112 36 call clear_target_state; and SXFRCTL0, ~SPIOEN; if ((ahc->features & AHC_QUEUE_REGS) == 0) { mov A, QINPOS; } poll_for_work_loop: if ((ahc->features & AHC_QUEUE_REGS) == 0) { and SEQCTL, ~PAUSEDIS; } test SSTAT0, SELDO|SELDI jnz selection; test SCSISEQ, ENSELO jnz poll_for_work; if ((ahc->features & AHC_TWIN) != 0) { /* * Twin channel devices cannot handle things like SELTO * interrupts on the "background" channel. So, if we * are selecting, keep polling the current channel util * either a selection or reselection occurs. */ xor SBLKCTL,SELBUSB; /* Toggle to the other bus */ test SSTAT0, SELDO|SELDI jnz selection; test SCSISEQ, ENSELO jnz poll_for_work; xor SBLKCTL,SELBUSB; /* Toggle back */ } cmp WAITING_SCBH,SCB_LIST_NULL jne start_waiting; test_queue: /* Has the driver posted any work for us? */ if ((ahc->features & AHC_QUEUE_REGS) != 0) { test QOFF_CTLSTA, SCB_AVAIL jz poll_for_work_loop; mov NONE, SNSCB_QOFF; inc QINPOS; } else { or SEQCTL, PAUSEDIS; cmp KERNEL_QINPOS, A je poll_for_work_loop; inc QINPOS; and SEQCTL, ~PAUSEDIS; } d116 51 a166 37 * SCBs in the list of SCBs awaiting selection. If we have * any SCBs available for use, pull the tag from the QINFIFO * and get to work on it. */ if ((ahc->flags & AHC_PAGESCBS) != 0) { mov ALLZEROS call get_free_or_disc_scb; } dequeue_scb: add A, -1, QINPOS; mvi QINFIFO_OFFSET call fetch_byte; if ((ahc->flags & AHC_PAGESCBS) == 0) { /* In the non-paging case, the SCBID == hardware SCB index */ mov SCBPTR, RETURN_2; } dma_queued_scb: /* * DMA the SCB from host ram into the current SCB location. */ mvi DMAPARAMS, HDMAEN|DIRECTION|FIFORESET; mov RETURN_2 call dma_scb; /* * Preset the residual fields in case we never go through a data phase. * This isn't done by the host so we can avoid a DMA to clear these * fields for the normal case of I/O that completes without underrun * or overrun conditions. */ if ((ahc->features & AHC_CMD_CHAN) != 0) { bmov SCB_RESID_DCNT, SCB_DATACNT, 3; } else { mov SCB_RESID_DCNT[0],SCB_DATACNT[0]; mov SCB_RESID_DCNT[1],SCB_DATACNT[1]; mov SCB_RESID_DCNT[2],SCB_DATACNT[2]; } mov SCB_RESID_SGCNT, SCB_SGCOUNT; d169 8 a176 13 /* * Place us on the waiting list in case our selection * doesn't win during bus arbitration. */ mov SCB_NEXT,WAITING_SCBH; mov WAITING_SCBH, SCBPTR; start_waiting: /* * Pull the first entry off of the waiting SCB list. */ mov SCBPTR, WAITING_SCBH; call start_selection; jmp poll_for_work; d178 6 d185 31 a215 43 if ((ahc->features & AHC_TWIN) != 0) { and SINDEX,~SELBUSB,SBLKCTL;/* Clear channel select bit */ and A,SELBUSB,SCB_TCL; /* Get new channel bit */ or SINDEX,A; mov SBLKCTL,SINDEX; /* select channel */ } initialize_scsiid: mov SINDEX, SCSISEQ_TEMPLATE; if ((ahc->flags & AHC_TARGETMODE) != 0) { test SCB_CONTROL, TARGET_SCB jz . + 4; if ((ahc->features & AHC_ULTRA2) != 0) { mov SCSIID_ULTRA2, SCB_CMDPTR[2]; } else { mov SCSIID, SCB_CMDPTR[2]; } or SINDEX, TEMODE; jmp initialize_scsiid_fini; } if ((ahc->features & AHC_ULTRA2) != 0) { and A, TID, SCB_TCL; /* Get target ID */ and SCSIID_ULTRA2, OID; /* Clear old target */ or SCSIID_ULTRA2, A; } else { and A, TID, SCB_TCL; /* Get target ID */ and SCSIID, OID; /* Clear old target */ or SCSIID, A; } initialize_scsiid_fini: mov SCSISEQ, SINDEX ret; /* * Initialize transfer settings and clear the SCSI channel. * SINDEX should contain any additional bit's the client wants * set in SXFRCTL0. We also assume that the current SCB is * a valid SCB for the target we wish to talk to. */ initialize_channel: or SXFRCTL0, CLRSTCNT|CLRCHN, SINDEX; set_transfer_settings: if ((ahc->features & AHC_ULTRA) != 0) { test SCB_CONTROL, ULTRAENB jz . + 2; or SXFRCTL0, FAST20; } d217 2 a218 1 * Initialize SCSIRATE with the appropriate value for this target. d220 8 a227 188 if ((ahc->features & AHC_ULTRA2) != 0) { bmov SCSIRATE, SCB_SCSIRATE, 2 ret; } else { mov SCSIRATE, SCB_SCSIRATE ret; } selection: test SSTAT0,SELDO jnz select_out; mvi CLRSINT0, CLRSELDI; select_in: if ((ahc->flags & AHC_TARGETMODE) != 0) { if ((ahc->flags & AHC_INITIATORMODE) != 0) { test SSTAT0, TARGET jz initiator_reselect; } /* * We've just been selected. Assert BSY and * setup the phase for receiving messages * from the target. */ mvi SCSISIGO, P_MESGOUT|BSYO; mvi CLRSINT1, CLRBUSFREE; /* * Setup the DMA for sending the identify and * command information. */ or SEQ_FLAGS, CMDPHASE_PENDING; mov A, TQINPOS; if ((ahc->features & AHC_CMD_CHAN) != 0) { mvi DINDEX, CCHADDR; mvi TMODE_CMDADDR call set_32byte_addr; mvi CCSCBCTL, CCSCBRESET; } else { mvi DINDEX, HADDR; mvi TMODE_CMDADDR call set_32byte_addr; mvi DFCNTRL, FIFORESET; } /* Initiator that selected us */ and SAVED_TCL, SELID_MASK, SELID; if ((ahc->features & AHC_CMD_CHAN) != 0) { mov CCSCBRAM, SAVED_TCL; } else { mov DFDAT, SAVED_TCL; } /* The Target ID we were selected at */ if ((ahc->features & AHC_CMD_CHAN) != 0) { if ((ahc->features & AHC_MULTI_TID) != 0) { and CCSCBRAM, OID, TARGIDIN; } else if ((ahc->features & AHC_ULTRA2) != 0) { and CCSCBRAM, OID, SCSIID_ULTRA2; } else { and CCSCBRAM, OID, SCSIID; } } else { if ((ahc->features & AHC_MULTI_TID) != 0) { and DFDAT, OID, TARGIDIN; } else if ((ahc->features & AHC_ULTRA2) != 0) { and DFDAT, OID, SCSIID_ULTRA2; } else { and DFDAT, OID, SCSIID; } } /* No tag yet */ mvi INITIATOR_TAG, SCB_LIST_NULL; /* * If ATN isn't asserted, the target isn't interested * in talking to us. Go directly to bus free. */ test SCSISIGI, ATNI jz target_busfree; /* * Watch ATN closely now as we pull in messages from the * initiator. We follow the guidlines from section 6.5 * of the SCSI-2 spec for what messages are allowed when. */ call target_inb; /* * Our first message must be one of IDENTIFY, ABORT, or * BUS_DEVICE_RESET. */ /* XXX May need to be more lax here for older initiators... */ test DINDEX, MSG_IDENTIFYFLAG jz host_target_message_loop; /* Store for host */ if ((ahc->features & AHC_CMD_CHAN) != 0) { mov CCSCBRAM, DINDEX; } else { mov DFDAT, DINDEX; } /* Remember for disconnection decision */ test DINDEX, MSG_IDENTIFY_DISCFLAG jnz . + 2; /* XXX Honor per target settings too */ or SEQ_FLAGS, NO_DISCONNECT; test SCSISIGI, ATNI jz ident_messages_done; call target_inb; /* * If this is a tagged request, the tagged message must * immediately follow the identify. We test for a valid * tag message by seeing if it is >= MSG_SIMPLE_Q_TAG and * < MSG_IGN_WIDE_RESIDUE. */ add A, -MSG_SIMPLE_Q_TAG, DINDEX; jnc ident_messages_done; add A, -MSG_IGN_WIDE_RESIDUE, DINDEX; jc ident_messages_done; /* Store for host */ if ((ahc->features & AHC_CMD_CHAN) != 0) { mov CCSCBRAM, DINDEX; } else { mov DFDAT, DINDEX; } /* * If the initiator doesn't feel like providing a tag number, * we've got a failed selection and must transition to bus * free. */ test SCSISIGI, ATNI jz target_busfree; /* * Store the tag for the host. */ call target_inb; if ((ahc->features & AHC_CMD_CHAN) != 0) { mov CCSCBRAM, DINDEX; } else { mov DFDAT, DINDEX; } mov INITIATOR_TAG, DINDEX; jmp ident_messages_done; /* * Pushed message loop to allow the kernel to * run it's own target mode message state engine. */ host_target_message_loop: mvi INTSTAT, HOST_MSG_LOOP; nop; cmp RETURN_1, EXIT_MSG_LOOP je target_ITloop; test SSTAT0, SPIORDY jz .; jmp host_target_message_loop; ident_messages_done: /* If ring buffer is full, return busy or queue full */ mov A, KERNEL_TQINPOS; cmp TQINPOS, A jne tqinfifo_has_space; mvi P_STATUS|BSYO call change_phase; cmp INITIATOR_TAG, SCB_LIST_NULL je . + 3; mvi STATUS_QUEUE_FULL call target_outb; jmp target_busfree_wait; mvi STATUS_BUSY call target_outb; jmp target_busfree_wait; tqinfifo_has_space: /* Terminate the ident list */ if ((ahc->features & AHC_CMD_CHAN) != 0) { mvi CCSCBRAM, SCB_LIST_NULL; } else { mvi DFDAT, SCB_LIST_NULL; } or SEQ_FLAGS, TARG_CMD_PENDING|IDENTIFY_SEEN; test SCSISIGI, ATNI jnz target_mesgout_pending_msg; jmp target_ITloop; /* * We carefully toggle SPIOEN to allow us to return the * message byte we receive so it can be checked prior to * driving REQ on the bus for the next byte. */ target_inb: /* * Drive REQ on the bus by enabling SCSI PIO. */ or SXFRCTL0, SPIOEN; /* Wait for the byte */ test SSTAT0, SPIORDY jz .; /* Prevent our read from triggering another REQ */ and SXFRCTL0, ~SPIOEN; /* Save latched contents */ mov DINDEX, SCSIDATL ret; } a228 1 if ((ahc->flags & AHC_INITIATORMODE) != 0) { d231 2 a232 1 * reselected, but haven't seen an IDENTIFY message from the target yet. d234 5 a238 17 initiator_reselect: /* XXX test for and handle ONE BIT condition */ and SAVED_TCL, SELID_MASK, SELID; if ((ahc->features & AHC_TWIN) != 0) { test SBLKCTL, SELBUSB jz . + 2; or SAVED_TCL, SELBUSB; } or SXFRCTL0, SPIOEN|CLRSTCNT|CLRCHN; mvi CLRSINT1,CLRBUSFREE; or SIMODE1, ENBUSFREE; /* * We aren't expecting a * bus free, so interrupt * the kernel driver if it * happens. */ jmp ITloop; } d241 1 a241 1 * After the selection, remove this SCB from the "waiting SCB" d246 15 a260 181 select_out: /* Turn off the selection hardware */ and SCSISEQ, ENSELI|ENRSELI|ENAUTOATNP, SCSISEQ_TEMPLATE; mvi CLRSINT0, CLRSELDO; mov SCBPTR, WAITING_SCBH; mov WAITING_SCBH,SCB_NEXT; mov SAVED_TCL, SCB_TCL; if ((ahc->flags & AHC_TARGETMODE) != 0) { test SSTAT0, TARGET jz initiator_select; /* * We've just re-selected an initiator. * Assert BSY and setup the phase for * sending our identify messages. */ mvi P_MESGIN|BSYO call change_phase; mvi CLRSINT1,CLRBUSFREE; /* * Start out with a simple identify message. */ and A, LID, SCB_TCL; or A, MSG_IDENTIFYFLAG call target_outb; /* * If we are the result of a tagged command, send * a simple Q tag and the tag id. */ test SCB_CONTROL, TAG_ENB jz . + 3; mvi MSG_SIMPLE_Q_TAG call target_outb; mov SCB_INITIATOR_TAG call target_outb; mov INITIATOR_TAG, SCB_INITIATOR_TAG; target_synccmd: /* * Now determine what phases the host wants us * to go through. */ mov SEQ_FLAGS, SCB_TARGET_PHASES; target_ITloop: /* * Start honoring ATN signals now that * we properly identified ourselves. */ test SCSISIGI, ATNI jnz target_mesgout; test SEQ_FLAGS, CMDPHASE_PENDING jnz target_cmdphase; test SEQ_FLAGS, DPHASE_PENDING jnz target_dphase; test SEQ_FLAGS, SPHASE_PENDING jnz target_sphase; /* * No more work to do. Either disconnect or not depending * on the state of NO_DISCONNECT. */ test SEQ_FLAGS, NO_DISCONNECT jz target_disconnect; if ((ahc->flags & AHC_PAGESCBS) != 0) { mov ALLZEROS call get_free_or_disc_scb; } mov RETURN_1, ALLZEROS; call complete_target_cmd; cmp RETURN_1, CONT_MSG_LOOP jne .; mvi DMAPARAMS, HDMAEN|DIRECTION|FIFORESET; mov SCB_TAG call dma_scb; jmp target_synccmd; target_mesgout: mvi SCSISIGO, P_MESGOUT|BSYO; call target_inb; /* Local Processing goes here... */ target_mesgout_pending_msg: jmp host_target_message_loop; target_disconnect: mvi P_MESGIN|BSYO call change_phase; test SEQ_FLAGS, DPHASE jz . + 2; mvi MSG_SAVEDATAPOINTER call target_outb; mvi MSG_DISCONNECT call target_outb; target_busfree_wait: /* Wait for preceeding I/O session to complete. */ test SCSISIGI, ACKI jnz .; target_busfree: clr SCSISIGO; mvi LASTPHASE, P_BUSFREE; call complete_target_cmd; jmp poll_for_work; target_cmdphase: mvi P_COMMAND|BSYO call change_phase; call target_inb; mov A, DINDEX; /* Store for host */ if ((ahc->features & AHC_CMD_CHAN) != 0) { mov CCSCBRAM, A; } else { mov DFDAT, A; } /* * Determine the number of bytes to read * based on the command group code via table lookup. * We reuse the first 8 bytes of the TARG_SCSIRATE * BIOS array for this table. Count is one less than * the total for the command since we've already fetched * the first byte. */ shr A, CMD_GROUP_CODE_SHIFT; add SINDEX, TARG_SCSIRATE, A; mov A, SINDIR; test A, 0xFF jz command_phase_done; command_loop: or SXFRCTL0, SPIOEN; test SSTAT0, SPIORDY jz .; cmp A, 1 jne . + 2; and SXFRCTL0, ~SPIOEN; /* Last Byte */ if ((ahc->features & AHC_CMD_CHAN) != 0) { mov CCSCBRAM, SCSIDATL; } else { mov DFDAT, SCSIDATL; } dec A; test A, 0xFF jnz command_loop; command_phase_done: and SEQ_FLAGS, ~CMDPHASE_PENDING; jmp target_ITloop; target_dphase: /* * Data direction flags are from the * perspective of the initiator. */ test SCB_TARGET_PHASES[1], TARGET_DATA_IN jz . + 4; mvi LASTPHASE, P_DATAOUT; mvi P_DATAIN|BSYO call change_phase; jmp . + 3; mvi LASTPHASE, P_DATAIN; mvi P_DATAOUT|BSYO call change_phase; mov ALLZEROS call initialize_channel; jmp p_data; target_sphase: mvi P_STATUS|BSYO call change_phase; mvi LASTPHASE, P_STATUS; mov SCB_TARGET_STATUS call target_outb; /* XXX Watch for ATN or parity errors??? */ mvi SCSISIGO, P_MESGIN|BSYO; /* MSG_CMDCMPLT is 0, but we can't do an immediate of 0 */ mov ALLZEROS call target_outb; jmp target_busfree_wait; complete_target_cmd: test SEQ_FLAGS, TARG_CMD_PENDING jnz . + 2; mov SCB_TAG jmp complete_post; if ((ahc->features & AHC_CMD_CHAN) != 0) { /* Set the valid byte */ mvi CCSCBADDR, 24; mov CCSCBRAM, ALLONES; mvi CCHCNT, 28; or CCSCBCTL, CCSCBEN|CCSCBRESET; test CCSCBCTL, CCSCBDONE jz .; clr CCSCBCTL; } else { /* Set the valid byte */ or DFCNTRL, FIFORESET; mvi DFWADDR, 3; /* Third 64bit word or byte 24 */ mov DFDAT, ALLONES; mvi HCNT[0], 28; clr HCNT[1]; clr HCNT[2]; or DFCNTRL, HDMAEN|FIFOFLUSH; call dma_finish; } inc TQINPOS; mvi INTSTAT,CMDCMPLT ret; } if ((ahc->flags & AHC_INITIATORMODE) != 0) { initiator_select: mvi SPIOEN call initialize_channel; d262 30 a291 20 /* * We aren't expecting a bus free, so interrupt * the kernel driver if it happens. */ mvi CLRSINT1,CLRBUSFREE; or SIMODE1, ENBUSFREE; /* * As soon as we get a successful selection, the target * should go into the message out phase since we have ATN * asserted. */ mvi MSG_OUT, MSG_IDENTIFYFLAG; or SEQ_FLAGS, IDENTIFY_SEEN; /* * Main loop for information transfer phases. Wait for the * target to assert REQ before checking MSG, C/D and I/O for * the bus phase. */ d293 2 a294 1 call phase_lock; d296 20 a315 42 mov A, LASTPHASE; test A, ~P_DATAIN jz p_data; cmp A,P_COMMAND je p_command; cmp A,P_MESGOUT je p_mesgout; cmp A,P_STATUS je p_status; cmp A,P_MESGIN je p_mesgin; mvi INTSTAT,BAD_PHASE; jmp ITloop; /* Try reading the bus again. */ await_busfree: and SIMODE1, ~ENBUSFREE; mov NONE, SCSIDATL; /* Ack the last byte */ and SXFRCTL0, ~SPIOEN; test SSTAT1,REQINIT|BUSFREE jz .; test SSTAT1, BUSFREE jnz poll_for_work; mvi INTSTAT, BAD_PHASE; } clear_target_state: /* * We assume that the kernel driver may reset us * at any time, even in the middle of a DMA, so * clear DFCNTRL too. */ clr DFCNTRL; /* * We don't know the target we will connect to, * so default to narrow transfers to avoid * parity problems. */ if ((ahc->features & AHC_ULTRA2) != 0) { bmov SCSIRATE, ALLZEROS, 2; } else { clr SCSIRATE; and SXFRCTL0, ~(FAST20); } mvi LASTPHASE, P_BUSFREE; /* clear target specific flags */ clr SEQ_FLAGS ret; d322 9 a330 28 if ((ahc->features & AHC_ULTRA2) != 0) { /* * The preload circuitry requires us to * reload the address too, so pull it from * the shaddow address. */ bmov HADDR, SHADDR, 4; bmov HCNT, SCB_RESID_DCNT, 3; } else if ((ahc->features & AHC_CMD_CHAN) != 0) { bmov STCNT, SCB_RESID_DCNT, 3; } else { mvi DINDEX, STCNT; mvi SCB_RESID_DCNT call bcopy_3; } and DATA_COUNT_ODD, 0x1, SCB_RESID_DCNT[0]; jmp data_phase_loop; p_data: if ((ahc->features & AHC_ULTRA2) != 0) { mvi DMAPARAMS, PRELOADEN|SCSIEN|HDMAEN; } else { mvi DMAPARAMS, WIDEODD|SCSIEN|SDMAEN|HDMAEN|FIFORESET; } test LASTPHASE, IOI jnz . + 2; or DMAPARAMS, DIRECTION; call assert; /* * Ensure entering a data * phase is okay - seen identify, etc. d332 2 a333 4 if ((ahc->features & AHC_CMD_CHAN) != 0) { mvi CCSGADDR, CCSGADDR_MAX; } test SEQ_FLAGS, DPHASE jnz data_phase_reinit; d335 3 a337 31 /* We have seen a data phase */ or SEQ_FLAGS, DPHASE; /* * Initialize the DMA address and counter from the SCB. * Also set SG_COUNT and SG_NEXT in memory since we cannot * modify the values in the SCB itself until we see a * save data pointers message. */ if ((ahc->features & AHC_CMD_CHAN) != 0) { bmov HADDR, SCB_DATAPTR, 7; } else { mvi DINDEX, HADDR; mvi SCB_DATAPTR call bcopy_7; } and DATA_COUNT_ODD, 0x1, SCB_DATACNT[0]; if ((ahc->features & AHC_ULTRA2) == 0) { if ((ahc->features & AHC_CMD_CHAN) != 0) { bmov STCNT, HCNT, 3; } else { call set_stcnt_from_hcnt; } } if ((ahc->features & AHC_CMD_CHAN) != 0) { bmov SG_COUNT, SCB_SGCOUNT, 5; } else { mvi DINDEX, SG_COUNT; mvi SCB_SGCOUNT call bcopy_5; } d341 1 a341 1 test SG_COUNT, 0xff jnz data_phase_inbounds; d348 5 a352 11 or SXFRCTL1,BITBUCKET; and DMAPARAMS, ~(HDMAEN|SDMAEN); if ((ahc->features & AHC_ULTRA2) != 0) { bmov HCNT, ALLONES, 3; } else if ((ahc->features & AHC_CMD_CHAN) != 0) { bmov STCNT, ALLONES, 3; } else { mvi STCNT[0], 0xFF; mvi STCNT[1], 0xFF; mvi STCNT[2], 0xFF; } d354 3 a356 11 /* If we are the last SG block, tell the hardware. */ cmp SG_COUNT,0x01 jne data_phase_wideodd; if ((ahc->features & AHC_ULTRA2) != 0) { or SG_CACHEPTR, LAST_SEG; } else { if ((ahc->flags & AHC_TARGETMODE) != 0) { test SSTAT0, TARGET jz . + 2; test DMAPARAMS, DIRECTION jz data_phase_wideodd; } and DMAPARAMS, ~WIDEODD; } d358 1 a358 10 if ((ahc->features & AHC_ULTRA2) != 0) { mov SINDEX, ALLONES; mov DFCNTRL, DMAPARAMS; test SSTAT0, SDONE jnz .;/* Wait for preload to complete */ data_phase_dma_loop: test SSTAT0, SDONE jnz data_phase_dma_done; test SSTAT1,PHASEMIS jz data_phase_dma_loop; /* ie. underrun */ } else { mov DMAPARAMS call dma; } a359 1 data_phase_dma_done: d361 1 a361 1 test SXFRCTL1,BITBUCKET jnz data_phase_overrun; d363 2 a364 4 /* See if we completed this segment */ test STCNT[0], 0xff jnz data_phase_finish; test STCNT[1], 0xff jnz data_phase_finish; test STCNT[2], 0xff jnz data_phase_finish; d370 7 a376 1 dec SG_COUNT; /* one less segment to go */ a377 1 test SG_COUNT, 0xff jz data_phase_finish; /* Are we done? */ d386 65 a450 74 if ((ahc->features & AHC_CMD_CHAN) != 0) { /* * Do we have any prefetch left??? */ cmp CCSGADDR, CCSGADDR_MAX jne prefetched_segs_avail; /* * Fetch MIN(CCSGADDR_MAX, (SG_COUNT * 8)) bytes. */ add A, -(CCSGRAM_MAXSEGS + 1), SG_COUNT; mvi A, CCSGADDR_MAX; jc . + 2; shl A, 3, SG_COUNT; mov CCHCNT, A; bmov CCHADDR, SG_NEXT, 4; mvi CCSGCTL, CCSGEN|CCSGRESET; test CCSGCTL, CCSGDONE jz .; and CCSGCTL, ~CCSGEN; test CCSGCTL, CCSGEN jnz .; mvi CCSGCTL, CCSGRESET; prefetched_segs_avail: bmov HADDR, CCSGRAM, 8; } else { mvi DINDEX, HADDR; mvi SG_NEXT call bcopy_4; mvi HCNT[0],SG_SIZEOF; clr HCNT[1]; clr HCNT[2]; or DFCNTRL, HDMAEN|DIRECTION|FIFORESET; call dma_finish; /* * Copy data from FIFO into SCB data pointer and data count. * This assumes that the SG segments are of the form: * struct ahc_dma_seg { * u_int32_t addr; four bytes, little-endian order * u_int32_t len; four bytes, little endian order * }; */ mvi HADDR call dfdat_in_7; } /* Track odd'ness */ test HCNT[0], 0x1 jz . + 2; xor DATA_COUNT_ODD, 0x1; if ((ahc->features & AHC_ULTRA2) == 0) { /* Load STCNT as well. It is a mirror of HCNT */ if ((ahc->features & AHC_CMD_CHAN) != 0) { bmov STCNT, HCNT, 3; } else { call set_stcnt_from_hcnt; } } /* Advance the SG pointer */ clr A; /* add sizeof(struct scatter) */ add SG_NEXT[0],SG_SIZEOF; adc SG_NEXT[1],A; if ((ahc->flags & AHC_TARGETMODE) != 0) { test SSTAT0, TARGET jnz data_phase_loop; } test SSTAT1, REQINIT jz .; test SSTAT1,PHASEMIS jz data_phase_loop; /* Ensure the last seg is visable at the shaddow layer */ if ((ahc->features & AHC_ULTRA2) != 0) { mov DFCNTRL, DMAPARAMS; test SSTAT0, SDONE jnz .;/* Wait for preload to complete */ } a452 3 if ((ahc->features & AHC_ULTRA2) != 0) { call ultra2_dmafinish; } d458 5 a462 25 if ((ahc->features & AHC_CMD_CHAN) != 0) { bmov SCB_RESID_DCNT, STCNT, 3; } else { mov SCB_RESID_DCNT[0],STCNT[0]; mov SCB_RESID_DCNT[1],STCNT[1]; mov SCB_RESID_DCNT[2],STCNT[2]; } mov SCB_RESID_SGCNT, SG_COUNT; if ((ahc->features & AHC_ULTRA2) != 0) { or SXFRCTL0, CLRSTCNT|CLRCHN; } if ((ahc->flags & AHC_TARGETMODE) != 0) { test SEQ_FLAGS, DPHASE_PENDING jz ITloop; and SEQ_FLAGS, ~DPHASE_PENDING; /* * For data-in phases, wait for any pending acks from the * initiator before changing phase. */ test DFCNTRL, DIRECTION jz target_ITloop; test SSTAT1, REQINIT jnz .; jmp target_ITloop; } jmp ITloop; a464 4 if ((ahc->features & AHC_ULTRA2) != 0) { call ultra2_dmafinish; or SXFRCTL0, CLRSTCNT|CLRCHN; } d468 3 a470 38 and SXFRCTL1, ~BITBUCKET; mvi INTSTAT,DATA_OVERRUN; jmp ITloop; ultra2_dmafinish: if ((ahc->features & AHC_ULTRA2) != 0) { test DFCNTRL, DIRECTION jnz ultra2_dmafifoempty; and DFCNTRL, ~SCSIEN; test DFCNTRL, SCSIEN jnz .; ultra2_dmafifoflush: or DFCNTRL, FIFOFLUSH; /* * The FIFOEMP status bit on the Ultra2 class * of controllers seems to be a bit flaky. * It appears that if the FIFO is full and the * transfer ends with some data in the REQ/ACK * FIFO, FIFOEMP will fall temporarily * as the data is transferred to the PCI bus. * This glitch lasts for fewer than 5 clock cycles, * so we work around the problem by ensuring the * status bit stays false through a full glitch * window. */ test DFSTATUS, FIFOEMP jz ultra2_dmafifoflush; test DFSTATUS, FIFOEMP jz ultra2_dmafifoflush; test DFSTATUS, FIFOEMP jz ultra2_dmafifoflush; test DFSTATUS, FIFOEMP jz ultra2_dmafifoflush; test DFSTATUS, FIFOEMP jz ultra2_dmafifoflush; ultra2_dmafifoempty: /* Don't clobber an inprogress host data transfer */ test DFSTATUS, MREQPEND jnz ultra2_dmafifoempty; ultra2_dmahalt: and DFCNTRL, ~(SCSIEN|HDMAEN); test DFCNTRL, HDMAEN jnz .; ret; } a471 1 if ((ahc->flags & AHC_INITIATORMODE) != 0) { d476 1 a476 1 call assert; d478 18 a495 50 if ((ahc->features & AHC_CMD_CHAN) != 0) { mov HCNT[0], SCB_CMDLEN; bmov HCNT[1], ALLZEROS, 2; if ((ahc->features & AHC_ULTRA2) == 0) { bmov STCNT, HCNT, 3; } add NONE, -17, SCB_CMDLEN; jc dma_cmd_data; /* * The data fifo seems to require 4 byte alligned * transfers from the sequencer. Force this to * be the case by clearing HADDR[0] even though * we aren't going to touch host memeory. */ bmov HADDR[0], ALLZEROS, 1; if ((ahc->features & AHC_ULTRA2) != 0) { mvi DFCNTRL, (PRELOADEN|SCSIEN|DIRECTION); } else { mvi DFCNTRL, (SCSIEN|SDMAEN|DIRECTION|FIFORESET); } bmov DFDAT, SCB_CMDSTORE, 16; jmp cmd_loop; dma_cmd_data: bmov HADDR, SCB_CMDPTR, 4; } else { mvi DINDEX, HADDR; mvi SCB_CMDPTR call bcopy_5; clr HCNT[1]; clr HCNT[2]; } if ((ahc->features & AHC_ULTRA2) == 0) { if ((ahc->features & AHC_CMD_CHAN) == 0) { call set_stcnt_from_hcnt; } mvi DFCNTRL, (SCSIEN|SDMAEN|HDMAEN|DIRECTION|FIFORESET); } else { mvi DFCNTRL, (PRELOADEN|SCSIEN|HDMAEN|DIRECTION); } cmd_loop: test SSTAT0, SDONE jnz . + 2; test SSTAT1, PHASEMIS jz cmd_loop; /* * Wait for our ACK to go-away on it's own * instead of being killed by SCSIEN getting cleared. */ test SCSISIGI, ACKI jnz .; and DFCNTRL, ~(SCSIEN|SDMAEN|HDMAEN); test DFCNTRL, (SCSIEN|SDMAEN|HDMAEN) jnz .; jmp ITloop; d502 2 a503 1 call assert; d505 7 a511 2 mov SCB_TARGET_STATUS, SCSIDATL; jmp ITloop; d513 1 d515 2 a516 22 * Message out phase. If MSG_OUT is MSG_IDENTIFYFLAG, build a full * indentify message sequence and send it to the target. The host may * override this behavior by setting the MK_MESSAGE bit in the SCB * control byte. This will cause us to interrupt the host and allow * it to handle the message phase completely on its own. If the bit * associated with this target is set, we will also interrupt the host, * thereby allowing it to send a message on the next selection regardless * of the transaction being sent. * * If MSG_OUT is == HOST_MSG, also interrupt the host and take a message. * This is done to allow the host to send messages outside of an identify * sequence while protecting the seqencer from testing the MK_MESSAGE bit * on an SCB that might not be for the current nexus. (For example, a * BDR message in responce to a bad reselection would leave us pointed to * an SCB that doesn't have anything to do with the current target). * * Otherwise, treat MSG_OUT as a 1 byte message to send (abort, abort tag, * bus device reset). * * When there are no messages to send, MSG_OUT should be set to MSG_NOOP, * in case the target decides to put us in this phase for some strange * reason. d518 3 a520 36 p_mesgout_retry: or SCSISIGO,ATNO,LASTPHASE;/* turn on ATN for the retry */ p_mesgout: mov SINDEX, MSG_OUT; cmp SINDEX, MSG_IDENTIFYFLAG jne p_mesgout_from_host; test SCB_CONTROL,MK_MESSAGE jnz host_message_loop; mov FUNCTION1, SCB_TCL; mov A, FUNCTION1; if ((ahc->features & AHC_HS_MAILBOX) != 0) { /* * Work around a pausing bug in at least the aic7890. * If the host needs to update the TARGET_MSG_REQUEST * bit field, it will set the HS_MAILBOX to 1. In * response, we pause with a specific interrupt code * asking for the mask to be updated before we continue. * Ugh. */ test HS_MAILBOX, 0xF0 jz . + 2; mvi INTSTAT, UPDATE_TMSG_REQ; nop; } mov SINDEX, TARGET_MSG_REQUEST[0]; if ((ahc->features & AHC_TWIN) != 0) { /* Second Channel uses high byte bits */ test SCB_TCL, SELBUSB jz . + 2; mov SINDEX, TARGET_MSG_REQUEST[1]; } else if ((ahc->features & AHC_WIDE) != 0) { test SCB_TCL, 0x80 jz . + 2; /* target > 7 */ mov SINDEX, TARGET_MSG_REQUEST[1]; } test SINDEX, A jnz host_message_loop; p_mesgout_identify: and SINDEX,LID,SCB_TCL; /* lun */ and A,DISCENB,SCB_CONTROL; /* mask off disconnect privledge */ or SINDEX,A; /* or in disconnect privledge */ or SINDEX,MSG_IDENTIFYFLAG; d522 5 a526 2 * Send a tag message if TAG_ENB is set in the SCB control block. * Use SCB_TAG (the position in the kernel's SCB array) as the tag value. d528 13 a540 20 p_mesgout_tag: test SCB_CONTROL,TAG_ENB jz p_mesgout_onebyte; mov SCSIDATL, SINDEX; /* Send the identify message */ call phase_lock; cmp LASTPHASE, P_MESGOUT jne p_mesgout_done; and SCSIDATL,TAG_ENB|SCB_TAG_TYPE,SCB_CONTROL; call phase_lock; cmp LASTPHASE, P_MESGOUT jne p_mesgout_done; mov SCB_TAG jmp p_mesgout_onebyte; /* * Interrupt the driver, and allow it to handle this message * phase and any required retries. */ p_mesgout_from_host: cmp SINDEX, HOST_MSG jne p_mesgout_onebyte; jmp host_message_loop; p_mesgout_onebyte: mvi CLRSINT1, CLRATNO; mov SCSIDATL, SINDEX; d543 1 a543 1 * If the next bus phase after ATN drops is message out, it means d546 3 a548 2 call phase_lock; cmp LASTPHASE, P_MESGOUT je p_mesgout_retry; d550 8 d559 2 a560 4 mvi CLRSINT1,CLRATNO; /* Be sure to turn ATNO off */ mov LAST_MSG, MSG_OUT; mvi MSG_OUT, MSG_NOOP; /* No message left */ jmp ITloop; d566 2 a567 1 mvi ACCUM call inb_first; /* read the 1st message byte */ d569 21 a589 21 test A,MSG_IDENTIFYFLAG jnz mesgin_identify; cmp A,MSG_DISCONNECT je mesgin_disconnect; cmp A,MSG_SAVEDATAPOINTER je mesgin_sdptrs; cmp ALLZEROS,A je mesgin_complete; cmp A,MSG_RESTOREPOINTERS je mesgin_rdptrs; cmp A,MSG_NOOP je mesgin_done; /* * Pushed message loop to allow the kernel to * run it's own message state engine. To avoid an * extra nop instruction after signaling the kernel, * we perform the phase_lock before checking to see * if we should exit the loop and skip the phase_lock * in the ITloop. Performing back to back phase_locks * shouldn't hurt, but why do it twice... */ host_message_loop: mvi INTSTAT, HOST_MSG_LOOP; call phase_lock; cmp RETURN_1, EXIT_MSG_LOOP je ITloop + 1; jmp host_message_loop; d592 2 a593 2 mov NONE,SCSIDATL; /*dummy read from latch to ACK*/ jmp ITloop; d598 16 a613 15 * We got a "command complete" message, so put the SCB_TAG into the QOUTFIFO, * and trigger a completion interrupt. Before doing so, check to see if there * is a residual or the status byte is something other than STATUS_GOOD (0). * In either of these conditions, we upload the SCB back to the host so it can * process this information. In the case of a non zero status byte, we * additionally interrupt the kernel driver synchronously, allowing it to * decide if sense should be retrieved. If the kernel driver wishes to request * sense, it will fill the kernel SCB with a request sense command and set * RETURN_1 to SEND_SENSE. If RETURN_1 is set to SEND_SENSE we redownload * the SCB, and process it as the next command by adding it to the waiting list. * If the kernel driver does not wish to request sense, it need only clear * RETURN_1, and the command is allowed to complete normally. We don't bother * to post to the QOUTFIFO in the error cases since it would require extra * work in the kernel driver to ensure that the entry was removed before the * command complete code tried processing it. d615 1 a615 1 d617 6 a622 1 * First check for residuals d624 3 a626 5 test SCB_RESID_SGCNT,0xff jnz upload_scb; test SCB_TARGET_STATUS,0xff jz complete; /* Good Status? */ upload_scb: mvi DMAPARAMS, FIFORESET; mov SCB_TAG call dma_scb; d628 35 a662 16 test SCB_TARGET_STATUS,0xff jz complete; /* Just a residual? */ mvi INTSTAT,BAD_STATUS; /* let driver know */ nop; cmp RETURN_1, SEND_SENSE jne complete; /* This SCB becomes the next to execute as it will retrieve sense */ mvi DMAPARAMS, HDMAEN|DIRECTION|FIFORESET; mov SCB_TAG call dma_scb; add_to_waiting_list: mov SCB_NEXT,WAITING_SCBH; mov WAITING_SCBH, SCBPTR; /* * Prepare our selection hardware before the busfree so we have a * high probability of winning arbitration. */ call start_selection; jmp await_busfree; d664 35 a698 28 complete: /* If we are untagged, clear our address up in host ram */ test SCB_CONTROL, TAG_ENB jnz complete_queue; mov A, SAVED_TCL; /* fvdl - let ahc_intr clear this to avoid race conditions */ /* mvi UNTAGGEDSCB_OFFSET call post_byte_setup; */ /* mvi SCB_LIST_NULL call post_byte; */ complete_queue: mov SCB_TAG call complete_post; jmp await_busfree; } complete_post: /* Post the SCBID in SINDEX and issue an interrupt */ call add_scb_to_free_list; mov ARG_1, SINDEX; if ((ahc->features & AHC_QUEUE_REGS) != 0) { mov A, SDSCB_QOFF; } else { mov A, QOUTPOS; } mvi QOUTFIFO_OFFSET call post_byte_setup; mov ARG_1 call post_byte; if ((ahc->features & AHC_QUEUE_REGS) == 0) { inc QOUTPOS; } mvi INTSTAT,CMDCMPLT ret; a699 1 if ((ahc->flags & AHC_INITIATORMODE) != 0) { d705 18 a722 3 or SCB_CONTROL,DISCONNECTED; call add_scb_to_disc_list; jmp await_busfree; d725 2 a726 5 * Save data pointers message: * Copying RAM values back to SCB, for Save Data Pointers message, but * only if we've actually been into a data phase to change them. This * protects against bogus data in scratch ram and the residual counts * since they are only initialized when we go into data_in or data_out. d729 2 a730 21 test SEQ_FLAGS, DPHASE jz mesgin_done; /* * The SCB SGPTR becomes the next one we'll download, * and the SCB DATAPTR becomes the current SHADDR. * Use the residual number since STCNT is corrupted by * any message transfer. */ if ((ahc->features & AHC_CMD_CHAN) != 0) { bmov SCB_SGCOUNT, SG_COUNT, 5; bmov SCB_DATAPTR, SHADDR, 4; bmov SCB_DATACNT, SCB_RESID_DCNT, 3; } else { mvi DINDEX, SCB_SGCOUNT; mvi SG_COUNT call bcopy_5; mvi DINDEX, SCB_DATAPTR; mvi SHADDR call bcopy_4; mvi SCB_RESID_DCNT call bcopy_3; } jmp mesgin_done; d739 2 a740 2 and SEQ_FLAGS, ~DPHASE; /* * We'll reload them a741 1 * the dataphase. d743 1 a743 1 jmp mesgin_done; d751 9 a759 21 if ((ahc->features & AHC_WIDE) != 0) { and A,0x0f; /* lun in lower four bits */ } else { and A,0x07; /* lun in lower three bits */ } or SAVED_TCL,A; /* SAVED_TCL should be complete now */ mvi ARG_2, SCB_LIST_NULL; /* SCBID of prev SCB in disc List */ call get_untagged_SCBID; cmp ARG_1, SCB_LIST_NULL je snoop_tag; if ((ahc->flags & AHC_PAGESCBS) != 0) { test SEQ_FLAGS, SCBPTR_VALID jz use_retrieveSCB; } /* * If the SCB was found in the disconnected list (as is * always the case in non-paging scenarios), SCBPTR is already * set to the correct SCB. So, simply setup the SCB and get * on with things. */ call rem_scb_from_disc_list; jmp setup_SCB; d762 2 a763 2 * If we get one, we use the tag returned to find the proper * SCB. With SCB paging, this requires using search for both tagged d768 1 a768 2 snoop_tag: mov NONE,SCSIDATL; /* ACK Identify MSG */ d770 5 a774 3 call phase_lock; cmp LASTPHASE, P_MESGIN jne not_found; cmp SCSIBUSL,MSG_SIMPLE_Q_TAG jne not_found; d776 9 a784 1 mvi ARG_1 call inb_next; /* tag value */ d786 8 a793 6 /* * Ensure that the SCB the tag points to is for * an SCB transaction to the reconnecting target. */ use_retrieveSCB: call retrieveSCB; d795 26 a820 20 mov A, SAVED_TCL; cmp SCB_TCL, A jne not_found_cleanup_scb; test SCB_CONTROL,DISCONNECTED jz not_found_cleanup_scb; and SCB_CONTROL,~DISCONNECTED; or SEQ_FLAGS,IDENTIFY_SEEN; /* make note of IDENTIFY */ call set_transfer_settings; /* See if the host wants to send a message upon reconnection */ test SCB_CONTROL, MK_MESSAGE jz mesgin_done; and SCB_CONTROL, ~MK_MESSAGE; mvi HOST_MSG call mk_mesg; jmp mesgin_done; not_found_cleanup_scb: test SCB_CONTROL, DISCONNECTED jz . + 3; call add_scb_to_disc_list; jmp not_found; call add_scb_to_free_list; not_found: mvi INTSTAT, NO_MATCH; jmp mesgin_done; d827 16 d847 16 a862 2 or SCSISIGO,ATNO,LASTPHASE;/* turn on ATNO */ mov MSG_OUT,SINDEX ret; d879 1 a879 3 inb_next_wait_perr: mvi INTSTAT, PERR_DETECTED; jmp inb_next_wait; d881 2 a882 1 mov NONE,SCSIDATL; /*dummy read from latch to ACK*/ d884 2 a885 10 /* * If there is a parity error, wait for the kernel to * see the interrupt and prepare our message response * before continuing. */ test SSTAT1, REQINIT jz inb_next_wait; test SSTAT1, SCSIPERR jnz inb_next_wait_perr; inb_next_check_phase: and LASTPHASE, PHASE_MASK, SCSISIGI; cmp LASTPHASE, P_MESGIN jne mesgin_phasemis; d887 3 a889 2 mov DINDEX,SINDEX; mov DINDIR,SCSIBUSL ret; /*read byte directly from bus*/ d891 1 a891 2 mov NONE,SCSIDATL ret; /*dummy read from latch to ACK*/ } a892 40 if ((ahc->flags & AHC_TARGETMODE) != 0) { /* * Change to a new phase. If we are changing the state of the I/O signal, * from out to in, wait an additional data release delay before continuing. */ change_phase: /* Wait for preceeding I/O session to complete. */ test SCSISIGI, ACKI jnz .; /* Change the phase */ and DINDEX, IOI, SCSISIGI; mov SCSISIGO, SINDEX; and A, IOI, SINDEX; /* * If the data direction has changed, from * out (initiator driving) to in (target driving), * we must waitat least a data release delay plus * the normal bus settle delay. [SCSI III SPI 10.11.0] */ cmp DINDEX, A je change_phase_wait; test SINDEX, IOI jz change_phase_wait; call change_phase_wait; change_phase_wait: nop; nop; nop; nop ret; /* * Send a byte to an initiator in Automatic PIO mode. */ target_outb: or SXFRCTL0, SPIOEN; test SSTAT0, SPIORDY jz .; mov SCSIDATL, SINDEX; test SSTAT0, SPIORDY jz .; and SXFRCTL0, ~SPIOEN ret; } d897 2 a898 2 mvi INTSTAT, MSGIN_PHASEMIS; jmp ITloop; d907 4 a910 5 mov DFCNTRL,SINDEX; dma_loop: test SSTAT0,DMADONE jnz dma_dmadone; test SSTAT1,PHASEMIS jz dma_loop; /* ie. underrun */ dma_phasemis: d920 5 a924 8 dma_checkfifo: test DFCNTRL,DIRECTION jnz dma_fifoempty; dma_fifoflush: test DFSTATUS,FIFOEMP jz dma_fifoflush; dma_fifoempty: /* Don't clobber an inprogress host data transfer */ test DFSTATUS, MREQPEND jnz dma_fifoempty; d929 5 a933 14 dma_dmadone: and DFCNTRL, ~(SCSIEN|SDMAEN|HDMAEN); dma_halt: /* * Some revisions of the aic7880 have a problem where, if the * data fifo is full, but the PCI input latch is not empty, * HDMAEN cannot be cleared. The fix used here is to attempt * to drain the data fifo until there is space for the input * latch to drain and HDMAEN de-asserts. */ if ((ahc->features & AHC_ULTRA2) == 0) { mov NONE, DFDAT; } test DFCNTRL, (SCSIEN|SDMAEN|HDMAEN) jnz dma_halt; d935 12 a946 1 ret; d953 2 a954 1 test SEQ_FLAGS,IDENTIFY_SEEN jnz return; /* seen IDENTIFY? */ d956 1 a956 1 mvi INTSTAT,NO_IDENT ret; /* no - tell the kernel */ d959 5 a963 5 * Locate a disconnected SCB either by SAVED_TCL (ARG_1 is SCB_LIST_NULL) * or by the SCBID ARG_1. The search begins at the SCB index passed in * via SINDEX which is an SCB that must be on the disconnected list. If * the SCB cannot be found, SINDEX will be SCB_LIST_NULL, otherwise, SCBPTR * is set to the proper SCB. d966 13 a978 37 mov SCBPTR,SINDEX; /* Initialize SCBPTR */ cmp ARG_1, SCB_LIST_NULL jne findSCB_by_SCBID; mov A, SAVED_TCL; mvi SCB_TCL jmp findSCB_loop; /* &SCB_TCL -> SINDEX */ findSCB_by_SCBID: mov A, ARG_1; /* Tag passed in ARG_1 */ mvi SCB_TAG jmp findSCB_loop; /* &SCB_TAG -> SINDEX */ findSCB_next: mov ARG_2, SCBPTR; cmp SCB_NEXT, SCB_LIST_NULL je notFound; mov SCBPTR,SCB_NEXT; dec SINDEX; /* Last comparison moved us too far */ findSCB_loop: cmp SINDIR, A jne findSCB_next; mov SINDEX, SCBPTR ret; notFound: mvi SINDEX, SCB_LIST_NULL ret; /* * Retrieve an SCB by SCBID first searching the disconnected list falling * back to DMA'ing the SCB down from the host. This routine assumes that * ARG_1 is the SCBID of interrest and that SINDEX is the position in the * disconnected list to start the search from. If SINDEX is SCB_LIST_NULL, * we go directly to the host for the SCB. */ retrieveSCB: test SEQ_FLAGS, SCBPTR_VALID jz retrieve_from_host; mov SCBPTR call findSCB; /* Continue the search */ cmp SINDEX, SCB_LIST_NULL je retrieve_from_host; /* * This routine expects SINDEX to contain the index of the SCB to be * removed, SCBPTR to be pointing to that SCB, and ARG_2 to be the * SCBID of the SCB just previous to this one in the list or SCB_LIST_NULL * if it is at the head. */ rem_scb_from_disc_list: d980 12 a991 5 cmp ARG_2, SCB_LIST_NULL je rHead; mov DINDEX, SCB_NEXT; mov SCBPTR, ARG_2; mov SCB_NEXT, DINDEX; mov SCBPTR, SINDEX ret; d993 42 a1034 1 mov DISCONNECTED_SCBH,SCB_NEXT ret; a1035 1 retrieve_from_host: d1037 4 a1040 2 * We didn't find it. Pull an SCB and DMA down the one we want. * We should never get here in the non-paging case. d1042 13 a1054 231 mov ALLZEROS call get_free_or_disc_scb; mvi DMAPARAMS, HDMAEN|DIRECTION|FIFORESET; /* Jump instead of call as we want to return anyway */ mov ARG_1 jmp dma_scb; /* * Determine whether a target is using tagged or non-tagged transactions * by first looking for a matching transaction based on the TCL and if * that fails, looking up this device in the host's untagged SCB array. * The TCL to search for is assumed to be in SAVED_TCL. The value is * returned in ARG_1 (SCB_LIST_NULL for tagged, SCBID for non-tagged). * The SCBPTR_VALID bit is set in SEQ_FLAGS if we found the information * in an SCB instead of having to go to the host. */ get_untagged_SCBID: cmp DISCONNECTED_SCBH, SCB_LIST_NULL je get_SCBID_from_host; mvi ARG_1, SCB_LIST_NULL; mov DISCONNECTED_SCBH call findSCB; cmp SINDEX, SCB_LIST_NULL je get_SCBID_from_host; or SEQ_FLAGS, SCBPTR_VALID;/* Was in disconnected list */ test SCB_CONTROL, TAG_ENB jnz . + 2; mov ARG_1, SCB_TAG ret; mvi ARG_1, SCB_LIST_NULL ret; /* * Fetch a byte from host memory given an index of (A + (256 * SINDEX)) * and a base address of SCBID_ADDR. The byte is returned in RETURN_2. */ fetch_byte: mov ARG_2, SINDEX; if ((ahc->features & AHC_CMD_CHAN) != 0) { mvi DINDEX, CCHADDR; mvi SCBID_ADDR call set_1byte_addr; mvi CCHCNT, 1; mvi CCSGCTL, CCSGEN|CCSGRESET; test CCSGCTL, CCSGDONE jz .; mvi CCSGCTL, CCSGRESET; bmov RETURN_2, CCSGRAM, 1 ret; } else { mvi DINDEX, HADDR; mvi SCBID_ADDR call set_1byte_addr; mvi HCNT[0], 1; clr HCNT[1]; clr HCNT[2]; mvi DFCNTRL, HDMAEN|DIRECTION|FIFORESET; call dma_finish; mov RETURN_2, DFDAT ret; } /* * Prepare the hardware to post a byte to host memory given an * index of (A + (256 * SINDEX)) and a base address of SCBID_ADDR. */ post_byte_setup: mov ARG_2, SINDEX; if ((ahc->features & AHC_CMD_CHAN) != 0) { mvi DINDEX, CCHADDR; mvi SCBID_ADDR call set_1byte_addr; mvi CCHCNT, 1; mvi CCSCBCTL, CCSCBRESET ret; } else { mvi DINDEX, HADDR; mvi SCBID_ADDR call set_1byte_addr; mvi HCNT[0], 1; clr HCNT[1]; clr HCNT[2]; mvi DFCNTRL, FIFORESET ret; } post_byte: if ((ahc->features & AHC_CMD_CHAN) != 0) { bmov CCSCBRAM, SINDEX, 1; or CCSCBCTL, CCSCBEN|CCSCBRESET; test CCSCBCTL, CCSCBDONE jz .; clr CCSCBCTL ret; } else { mov DFDAT, SINDEX; or DFCNTRL, HDMAEN|FIFOFLUSH; jmp dma_finish; } get_SCBID_from_host: mov A, SAVED_TCL; mvi UNTAGGEDSCB_OFFSET call fetch_byte; mov RETURN_1, RETURN_2 ret; phase_lock_perr: mvi INTSTAT, PERR_DETECTED; phase_lock: /* * If there is a parity error, wait for the kernel to * see the interrupt and prepare our message response * before continuing. */ test SSTAT1, REQINIT jz phase_lock; test SSTAT1, SCSIPERR jnz phase_lock_perr; phase_lock_latch_phase: and SCSISIGO, PHASE_MASK, SCSISIGI; and LASTPHASE, PHASE_MASK, SCSISIGI ret; if ((ahc->features & AHC_CMD_CHAN) == 0) { set_stcnt_from_hcnt: mov STCNT[0], HCNT[0]; mov STCNT[1], HCNT[1]; mov STCNT[2], HCNT[2] ret; bcopy_7: mov DINDIR, SINDIR; mov DINDIR, SINDIR; bcopy_5: mov DINDIR, SINDIR; bcopy_4: mov DINDIR, SINDIR; bcopy_3: mov DINDIR, SINDIR; mov DINDIR, SINDIR; mov DINDIR, SINDIR ret; } if ((ahc->flags & AHC_TARGETMODE) != 0) { /* * Setup addr assuming that A is an index into * an array of 32byte objects, SINDEX contains * the base address of that array, and DINDEX * contains the base address of the location * to store the indexed address. */ set_32byte_addr: shr ARG_2, 3, A; shl A, 5; jmp set_1byte_addr; } /* * Setup addr assuming that A is an index into * an array of 64byte objects, SINDEX contains * the base address of that array, and DINDEX * contains the base address of the location * to store the indexed address. */ set_64byte_addr: shr ARG_2, 2, A; shl A, 6; /* * Setup addr assuming that A + (ARG_1 * 256) is an * index into an array of 1byte objects, SINDEX contains * the base address of that array, and DINDEX contains * the base address of the location to store the computed * address. */ set_1byte_addr: add DINDIR, A, SINDIR; mov A, ARG_2; adc DINDIR, A, SINDIR; clr A; adc DINDIR, A, SINDIR; adc DINDIR, A, SINDIR ret; /* * Either post or fetch and SCB from host memory based on the * DIRECTION bit in DMAPARAMS. The host SCB index is in SINDEX. */ dma_scb: mov A, SINDEX; if ((ahc->features & AHC_CMD_CHAN) != 0) { mvi DINDEX, CCHADDR; mvi HSCB_ADDR call set_64byte_addr; mov CCSCBPTR, SCBPTR; test DMAPARAMS, DIRECTION jz dma_scb_tohost; mvi CCHCNT, SCB_64BYTE_SIZE; mvi CCSCBCTL, CCARREN|CCSCBEN|CCSCBDIR|CCSCBRESET; cmp CCSCBCTL, CCSCBDONE|ARRDONE|CCARREN|CCSCBEN|CCSCBDIR jne .; jmp dma_scb_finish; dma_scb_tohost: mvi CCHCNT, SCB_32BYTE_SIZE; if ((ahc->chip & AHC_CHIPID_MASK) == AHC_AIC7895) { mvi CCSCBCTL, CCSCBRESET; bmov CCSCBRAM, SCB_CONTROL, SCB_32BYTE_SIZE; or CCSCBCTL, CCSCBEN|CCSCBRESET; test CCSCBCTL, CCSCBDONE jz .; } else { mvi CCSCBCTL, CCARREN|CCSCBEN|CCSCBRESET; cmp CCSCBCTL, CCSCBDONE|ARRDONE|CCARREN|CCSCBEN jne .; } dma_scb_finish: clr CCSCBCTL; test CCSCBCTL, CCARREN|CCSCBEN jnz .; ret; } else { mvi DINDEX, HADDR; mvi HSCB_ADDR call set_64byte_addr; mvi HCNT[0], SCB_32BYTE_SIZE; clr HCNT[1]; clr HCNT[2]; mov DFCNTRL, DMAPARAMS; test DMAPARAMS, DIRECTION jnz dma_scb_fromhost; /* Fill it with the SCB data */ copy_scb_tofifo: mvi SINDEX, SCB_CONTROL; add A, SCB_32BYTE_SIZE, SINDEX; copy_scb_tofifo_loop: mov DFDAT,SINDIR; mov DFDAT,SINDIR; mov DFDAT,SINDIR; mov DFDAT,SINDIR; mov DFDAT,SINDIR; mov DFDAT,SINDIR; mov DFDAT,SINDIR; cmp SINDEX, A jne copy_scb_tofifo_loop; or DFCNTRL, HDMAEN|FIFOFLUSH; dma_scb_fromhost: call dma_finish; /* If we were putting the SCB, we are done */ test DMAPARAMS, DIRECTION jz return; mvi SCB_CONTROL call dfdat_in_7; call dfdat_in_7_continued; call dfdat_in_7_continued; jmp dfdat_in_7_continued; dfdat_in_7: mov DINDEX,SINDEX; dfdat_in_7_continued: mov DINDIR,DFDAT; mov DINDIR,DFDAT; mov DINDIR,DFDAT; mov DINDIR,DFDAT; mov DINDIR,DFDAT; mov DINDIR,DFDAT; mov DINDIR,DFDAT ret; } d1057 1 a1057 2 * Wait for DMA from host memory to data FIFO to complete, then disable * DMA and wait for it to acknowledge that it's off. d1059 3 a1061 33 dma_finish: test DFSTATUS,HDONE jz dma_finish; /* Turn off DMA */ and DFCNTRL, ~HDMAEN; test DFCNTRL, HDMAEN jnz .; ret; add_scb_to_free_list: if ((ahc->flags & AHC_PAGESCBS) != 0) { mov SCB_NEXT, FREE_SCBH; mvi SCB_TAG, SCB_LIST_NULL; mov FREE_SCBH, SCBPTR ret; } else { mvi SCB_TAG, SCB_LIST_NULL ret; } if ((ahc->flags & AHC_PAGESCBS) != 0) { get_free_or_disc_scb: cmp FREE_SCBH, SCB_LIST_NULL jne dequeue_free_scb; cmp DISCONNECTED_SCBH, SCB_LIST_NULL jne dequeue_disc_scb; return_error: mvi SINDEX, SCB_LIST_NULL ret; dequeue_disc_scb: mov SCBPTR, DISCONNECTED_SCBH; dma_up_scb: mvi DMAPARAMS, FIFORESET; mov SCB_TAG call dma_scb; unlink_disc_scb: mov DISCONNECTED_SCBH, SCB_NEXT ret; dequeue_free_scb: mov SCBPTR, FREE_SCBH; mov FREE_SCBH, SCB_NEXT ret; } a1062 1 add_scb_to_disc_list: d1064 3 a1066 3 * Link this SCB into the DISCONNECTED list. This list holds the * candidates for paging out an SCB if one is needed for a new command. * Modifying the disconnected list is a critical(pause dissabled) section. d1068 6 a1073 2 mov SCB_NEXT, DISCONNECTED_SCBH; mov DISCONNECTED_SCBH, SCBPTR ret; @ 1.9 log @Patches from Justin that handle some REV B AIC7880 problems @ text @d1 1 a1 1 /* $NetBSD: aic7xxx.seq,v 1.8 1998/04/16 07:07:37 leo Exp $ */ d908 3 a910 3 dma_loop: test SSTAT0,DMADONE jnz dma_checkfifo test SSTAT1,PHASEMIS jz dma_loop /* ie. underrun */ d920 5 a924 8 dma_checkfifo: test DFCNTRL,DIRECTION jnz dma_fifoempty dma_fifoflush: test DFSTATUS,FIFOEMP jz dma_fifoflush dma_fifoempty: /* Don't clobber an inprogress host data transfer */ test DFSTATUS, MREQPEND jnz dma_fifoempty d926 1 a926 1 * Now shut the DMA enables off and make sure that the DMA enables are d929 5 a933 12 dma_dmadone: and DFCNTRL, 0xc7 /* ~(SCSIEN|SDMAEN|HDMAEN); */ dma_halt: /* * Some revisions of the aic7880 have a problem where, if the * data fifo is full, but the PCI input latch is not empty, * HDMAEN cannot be cleared. The fix used here is to attempt * to drain the data fifo until there is space for the input * latch to drain and HDMAEN de-asserts. */ mov NONE, DFDAT test DFCNTRL, HDMAEN jnz dma_halt @ 1.8 log @Backout endian changes here (per discussion with Justin) and move them to aic7xxx.c @ text @d1 1 a1 1 /* $NetBSD: aic7xxx.seq,v 1.6 1996/10/08 03:04:06 gibbs Exp $ */ d46 1 a46 1 VERSION AIC7XXX_SEQ_VER "$NetBSD: aic7xxx.seq,v 1.6 1996/10/08 03:04:06 gibbs Exp $" d908 3 a910 3 dma1: test SSTAT0,DMADONE jnz dma3 test SSTAT1,PHASEMIS jz dma1 /* ie. underrun */ d920 8 a927 5 dma3: test SINDEX,DIRECTION jnz dma5 dma4: test DFSTATUS,FIFOEMP jz dma4 d929 1 a929 1 * Now shut the DMA enables off and make sure that the DMA enables are d932 12 a943 5 dma5: /* disable DMA, but maintain WIDEODD */ and DFCNTRL,WIDEODD dma6: test DFCNTRL,0x38 jnz dma6 /* SCSIENACK|SDMAENACK|HDMAENACK */ @ 1.7 log @Make it possible to interface to big-endian cpu's. @ text @a427 6 #if BIG_ENDIAN_CPU mov HADDR3,DFDAT mov HADDR2,DFDAT mov HADDR1,DFDAT mov HADDR0,DFDAT #else a431 1 #endif a441 6 #if BIG_ENDIAN_CPU mov HCNT2,DFDAT /* LEO: MSB unused */ mov HCNT2,DFDAT mov HCNT1,DFDAT mov HCNT0,DFDAT #else a444 1 #endif a480 6 #if BIG_ENDIAN_CPU mov HADDR3, SCB_CMDPTR0 mov HADDR2, SCB_CMDPTR1 mov HADDR1, SCB_CMDPTR2 mov HADDR0, SCB_CMDPTR3 #else a484 1 #endif a1016 9 #if BIG_ENDIAN_CPU mov HADDR3, SCB_DATAPTR0 mov HADDR2, SCB_DATAPTR1 mov HADDR1, SCB_DATAPTR2 mov HADDR0, SCB_DATAPTR3 mov HCNT0, SCB_DATACNT3 mov HCNT1, SCB_DATACNT2 mov HCNT2, SCB_DATACNT1 #else a1023 1 #endif a1030 6 #if BIG_ENDIAN_CPU mov SG_NEXT3, SCB_SGPTR0 mov SG_NEXT2, SCB_SGPTR1 mov SG_NEXT1, SCB_SGPTR2 mov SG_NEXT0, SCB_SGPTR3 ret #else a1034 1 #endif a1045 10 #if BIG_ENDIAN_CPU mov SCB_SGPTR3,SG_NEXT0 mov SCB_SGPTR2,SG_NEXT1 mov SCB_SGPTR1,SG_NEXT2 mov SCB_SGPTR0,SG_NEXT3 mov SCB_DATAPTR3,SHADDR0 mov SCB_DATAPTR2,SHADDR1 mov SCB_DATAPTR1,SHADDR2 mov SCB_DATAPTR0,SHADDR3 #else d1050 1 a1054 2 #endif a1058 5 #if BIG_ENDIAN_CPU mov SCB_DATACNT3,SCB_RESID_DCNT0 mov SCB_DATACNT2,SCB_RESID_DCNT1 mov SCB_DATACNT1,SCB_RESID_DCNT2 ret #else a1061 1 #endif @ 1.6 log @dev/microcode/aic7xxx/aic7xxx.seq, dev/microcode/aic7xxx_seq.h, dev/ic/aic7xxxreg.h: Remove intrinsic knowledge about SDTR and WDTR messages and replace it with a generic message system that allows the kernel driver to handle SDTR, WDTR and any other type of extended message it chooses too. This makes the sequencer code much simpler, makes extended message handling debuggable since the bulk of the work is in the kernel driver, and saves lots of instruction space. Regen microcode header file. dev/ic/aic7xxx.c, dev/ic/aic7xxxvar.h: Add code to handle WDTR and SDTR negotiation in light of the changes in the message interface to the sequencer. Don't reject targets that negotiate async by sending an SDTR with a 0 offset. Use an sdtr message with 0,0 to negotiate async when a target suggests a period that is too long for us to handle. Some tape and cdrom drives don't like us doing the message reject that we did in the past. Fix a problem with handing the QUEUE FULL condition. Fix a race condition (most likely the cause of the SCB paging problems) that might allow the sequencer to get unpaused before the condition that caused it to be paused (a SEQINT) was handled. Race condition pointed out by Doug Ledford and by "Dan Willis" . dev/pci/ahc_pci.c: Add support for the 2940AU, an aic7860 based controller. dev/pci/pcidevs.h, dev/pci/pcidevs_data.h: Add product IDs for the 2940AU, aic7860 and aic7855. Regen data file. scsi/scsi_message.h: Add MSG_EXT_SDTR_LEN and MSG_EXT_WDTR_LEN - the length of bytes in these extended messages. Thanks to Chuck Cranor for testing these changes out for me. @ text @d1 1 a1 1 /* $NetBSD: aic7xxx.seq,v 1.5 1996/08/10 08:36:58 mycroft Exp $ */ d46 1 a46 1 VERSION AIC7XXX_SEQ_VER "$NetBSD: aic7xxx.seq,v 1.5 1996/08/10 08:36:58 mycroft Exp $" d50 1 a50 1 #include "../../../scsi/scsi_message.h" d428 6 d438 1 d449 6 d458 1 d495 6 d505 1 d1038 9 d1054 1 d1062 6 d1072 1 d1084 10 a1097 1 d1102 2 d1108 5 d1116 1 @ 1.5 log @Compile the sequencer statically. @ text @d1 1 a1 1 /* $NetBSD: aic7xxx.seq,v 1.4 1996/07/10 22:51:23 explorer Exp $ */ d46 1 a46 1 VERSION AIC7XXX_SEQ_VER "$NetBSD: aic7xxx.seq,v 1.4 1996/07/10 22:51:23 explorer Exp $" d50 1 d53 1 a194 10 test SCB_CMDLEN,0xff jnz mk_identify /* 0 Length Command? */ /* * The kernel has sent us an SCB with no command attached. This implies * that the kernel wants to send a message of some sort to this target, * so we interrupt the driver, allow it to fill the message buffer, and * then go back into the arbitration loop */ mvi INTSTAT,AWAITING_MSG jmp wait_for_selection d201 1 a201 1 or MSG0,MSG_IDENTIFY a203 1 test SCB_CONTROL,0xb0 jz !message /* WDTR, SDTR or TAG?? */ a207 1 d209 1 a210 1 test SCB_CONTROL,TAG_ENB jz mk_tag_done d216 6 a221 1 mk_tag_done: d223 1 a223 2 test SCB_CONTROL,0x90 jz !message /* NEEDWDTR|NEEDSDTR */ mov DINDEX call mk_dtr /* build DTR message if needed */ a224 1 !message: d511 1 a511 1 mvi MSG_NOP call mk_mesg /* build NOP message */ d569 1 a569 1 test A,MSG_IDENTIFY jnz mesgin_identify d571 1 a571 1 cmp A,MSG_SDPTRS je mesgin_sdptrs d573 1 a573 1 cmp A,MSG_RDPTRS je mesgin_rdptrs d575 1 a575 1 cmp A,MSG_REJECT je mesgin_reject d589 1 a589 1 mvi MSG_REJECT call mk_mesg d665 4 a668 5 * Is it an extended message? We only support the synchronous and wide data * transfer request messages, which will probably be in response to * WDTR or SDTR message outs from us. If it's not SDTR or WDTR, reject it - * apparently this can be done after any message in byte, according * to the SCSI-2 spec. d671 26 a696 38 mvi ARG_1 call inb_next /* extended message length */ mvi REJBYTE_EXT call inb_next /* extended message code */ cmp REJBYTE_EXT,MSG_SDTR je p_mesginSDTR cmp REJBYTE_EXT,MSG_WDTR je p_mesginWDTR jmp rej_mesgin p_mesginWDTR: cmp ARG_1,2 jne rej_mesgin /* extended mesg length=2 */ mvi ARG_1 call inb_next /* Width of bus */ mvi INTSTAT,WDTR_MSG /* let driver know */ test RETURN_1,0xff jz mesgin_done /* Do we need to send WDTR? */ cmp RETURN_1,SEND_REJ je rej_mesgin /* * Bus width was too large * Reject it. */ /* We didn't initiate the wide negotiation, so we must respond to the request */ and RETURN_1,0x7f /* Clear the SEND_WDTR Flag */ mvi DINDEX,MSG0 mvi MSG0 call mk_wdtr /* build WDTR message */ or SCSISIGO,ATNO /* turn on ATNO */ jmp mesgin_done p_mesginSDTR: cmp ARG_1,3 jne rej_mesgin /* extended mesg length=3 */ mvi ARG_1 call inb_next /* xfer period */ mvi A call inb_next /* REQ/ACK offset */ mvi INTSTAT,SDTR_MSG /* call driver to convert */ test RETURN_1,0xff jz mesgin_done /* Do we need to mk_sdtr/rej */ cmp RETURN_1,SEND_REJ je rej_mesgin /* * Requested SDTR too small * Reject it. */ clr ARG_1 /* Use the scratch ram rate */ mvi DINDEX, MSG0 mvi MSG0 call mk_sdtr d770 5 a774 5 test SSTAT1,BUSFREE jnz use_findSCB test SSTAT1,REQINIT jz snoop_tag_loop test SSTAT1,PHASEMIS jnz use_findSCB mvi A call inb_first cmp A,MSG_SIMPLE_TAG jne use_findSCB d934 1 a934 1 a1073 65 /* * If we need to negotiate transfer parameters, build the WDTR or SDTR message * starting at the address passed in SINDEX. DINDEX is modified on return. * The SCSI-II spec requires that Wide negotiation occur first and you can * only negotiat one or the other at a time otherwise in the event of a message * reject, you wouldn't be able to tell which message was the culpret. */ mk_dtr: test SCB_CONTROL,NEEDWDTR jnz mk_wdtr_16bit mvi ARG_1, MAXOFFSET /* Force an offset of 15 or 8 if WIDE */ mk_sdtr: mvi DINDIR,1 /* extended message */ mvi DINDIR,3 /* extended message length = 3 */ mvi DINDIR,1 /* SDTR code */ call sdtr_to_rate mov DINDIR,RETURN_1 /* REQ/ACK transfer period */ cmp ARG_1, MAXOFFSET je mk_sdtr_max_offset and DINDIR,0x0f,SINDIR /* Sync Offset */ mk_sdtr_done: add MSG_LEN,COMP_MSG0,DINDEX ret /* update message length */ mk_sdtr_max_offset: /* * We're initiating sync negotiation, so request the max offset we can (15 or 8) */ /* Talking to a WIDE device? */ test SCSIRATE, WIDEXFER jnz wmax_offset mvi DINDIR, MAX_OFFSET_8BIT jmp mk_sdtr_done wmax_offset: mvi DINDIR, MAX_OFFSET_16BIT jmp mk_sdtr_done mk_wdtr_16bit: mvi ARG_1,BUS_16_BIT mk_wdtr: mvi DINDIR,1 /* extended message */ mvi DINDIR,2 /* extended message length = 2 */ mvi DINDIR,3 /* WDTR code */ mov DINDIR,ARG_1 /* bus width */ add MSG_LEN,COMP_MSG0,DINDEX ret /* update message length */ sdtr_to_rate: call ndx_dtr /* index scratch space for target */ shr A,SINDIR,0x4 dec SINDEX /* Preserve SINDEX */ and A,0x7 clr RETURN_1 sdtr_to_rate_loop: test A,0x0f jz sdtr_to_rate_done add RETURN_1,0x19 dec A jmp sdtr_to_rate_loop sdtr_to_rate_done: shr RETURN_1,0x2 add RETURN_1,0x19 test SXFRCTL0,ULTRAEN jz return shr RETURN_1,0x1 return: ret @ 1.4 log @Updates to aic7xxx driver ; from pr port-i386/2600 @ text @d1 1 a1 1 /* $NetBSD$ */ d46 1 a46 1 VERSION AIC7XXX_SEQ_VER "$NetBSD$" d49 1 a49 1 #include "../../../../dev/ic/aic7xxxreg.h" @ 1.3 log @RCS Id police. @ text @d1 1 a1 1 /* $NetBSD: $ */ d42 2 d46 1 a46 1 VERSION AIC7XXX_SEQ_VER "$NetBSD: aic7xxx.seq,v 1.2 1996/05/16 03:51:45 mycroft Exp $" d90 5 d269 17 a291 1 d315 1 d347 14 d367 3 d471 8 d538 1 d908 1 @ 1.3.4.1 log @Pulled up from rev 1.4, ahc fixes @ text @d1 1 a1 1 /* $NetBSD: aic7xxx.seq,v 1.4 1996/07/10 22:51:23 explorer Exp $ */ a41 2 * from Id: aic7xxx.seq,v 1.42 1996/06/09 17:29:11 gibbs Exp * d44 1 a44 1 VERSION AIC7XXX_SEQ_VER "$NetBSD: aic7xxx.seq,v 1.4 1996/07/10 22:51:23 explorer Exp $" a87 5 clr SCSIRATE /* * We don't know the target we will * connect to, so default to narrow * transfers to avoid parity problems. */ a261 17 /* * Initialize Ultra mode setting. */ mov FUNCTION1,SCSIID mov A,FUNCTION1 and SINDEX,0xdf,SXFRCTL0 /* default to Ultra disabled */ test SCSIID, 0x80 jnz ultra_b /* Target ID > 7 */ test SBLKCTL, SELBUSB jnz ultra_b /* Second channel device */ test ULTRA_ENB,A jz set_sxfrctl0 or SINDEX, ULTRAEN jmp set_sxfrctl0 ultra_b: test ULTRA_ENB_B,A jz set_sxfrctl0 or SINDEX, ULTRAEN set_sxfrctl0: mov SXFRCTL0,SINDEX d268 1 a291 1 jmp ITloop /* Try reading the bus again. */ a322 14 /* Guard against overruns */ test SG_COUNT, 0xff jnz data_phase_inbounds /* * Turn on 'Bit Bucket' mode, set the transfer count to * 16meg and let the target run until it changes phase. * When the transfer completes, notify the host that we * had an overrun. */ or SXFRCTL1,BITBUCKET mvi STCNT0,0xff mvi STCNT1,0xff mvi STCNT2,0xff data_phase_inbounds: a328 3 /* Go tell the host about any overruns */ test SXFRCTL1,BITBUCKET jnz data_phase_overrun a429 8 data_phase_overrun: /* * Turn off BITBUCKET mode and notify the host */ and SXFRCTL1,0x7f /* ~BITBUCKET */ mvi INTSTAT,DATA_OVERRUN jmp ITloop a488 1 test SSTAT1,PHASEMIS jnz p_mesgout_phasemis a857 1 test SSTAT1,PHASEMIS jnz mesgin_phasemis @ 1.3.4.2 log @Pull up latest ahc driver. Fixes several bugs. @ text @d1 1 a1 1 /* $NetBSD: aic7xxx.seq,v 1.6 1996/10/08 03:04:06 gibbs Exp $ */ d46 1 a46 1 VERSION AIC7XXX_SEQ_VER "$NetBSD: aic7xxx.seq,v 1.6 1996/10/08 03:04:06 gibbs Exp $" d49 1 a49 2 #include "../../ic/aic7xxxreg.h" #include "../../../scsi/scsi_message.h" a51 1 #include "../../scsi/scsi_message.h" d193 10 d209 1 a209 1 or MSG0,MSG_IDENTIFYFLAG d212 1 d217 1 a218 1 test SCB_CONTROL,TAG_ENB jz mk_message d220 1 d226 1 a226 6 /* * Interrupt the driver, and allow it to tweak the message buffer * if it asks. */ mk_message: test SCB_CONTROL,MK_MESSAGE jz wait_for_selection d228 2 a229 1 mvi INTSTAT,AWAITING_MSG d231 1 d518 1 a518 1 mvi MSG_NOOP call mk_mesg /* build NOP message */ d576 1 a576 1 test A,MSG_IDENTIFYFLAG jnz mesgin_identify d578 1 a578 1 cmp A,MSG_SAVEDATAPOINTER je mesgin_sdptrs d580 1 a580 1 cmp A,MSG_RESTOREPOINTERS je mesgin_rdptrs d582 1 a582 1 cmp A,MSG_MESSAGE_REJECT je mesgin_reject d596 1 a596 1 mvi MSG_MESSAGE_REJECT call mk_mesg d672 5 a676 4 * Is it an extended message? Copy the message to our message buffer and * notify the host. The host will tell us whether to reject this message, * respond to it with the message that the host placed in our message buffer, * or simply to do nothing. d679 38 a716 26 mvi MSGIN_EXT_LEN call inb_next mvi MSGIN_EXT_OPCODE call inb_next mov A, MSGIN_EXT_LEN dec A /* Length counts the op code */ mvi SINDEX, MSGIN_EXT_BYTE0 mesgin_extended_loop: test A, 0xFF jz mesgin_extended_intr cmp SINDEX, MSGIN_EXT_LASTBYTE je mesgin_extended_dump call inb_next dec A /* * We pass the arg to inb in SINDEX, but DINDEX is the one incremented * so update SINDEX with DINDEX's value before looping again. */ mov DINDEX jmp mesgin_extended_loop mesgin_extended_dump: /* We have no more storage space, so dump the rest */ test A, 0xFF jz mesgin_extended_intr mvi NONE call inb_next dec A jmp mesgin_extended_dump mesgin_extended_intr: mvi INTSTAT,EXTENDED_MSG /* let driver know */ cmp RETURN_1,SEND_REJ je rej_mesgin cmp RETURN_1,SEND_MSG jne mesgin_done /* The kernel has setup a message to be sent */ d790 5 a794 5 test SSTAT1,BUSFREE jnz use_findSCB test SSTAT1,REQINIT jz snoop_tag_loop test SSTAT1,PHASEMIS jnz use_findSCB mvi A call inb_first cmp A,MSG_SIMPLE_Q_TAG jne use_findSCB d954 1 a954 1 return: d1094 65 @ 1.2 log @New version, with changes from Justin Gibbs and Noriyuki Soda. @ text @d1 2 d44 1 a44 1 VERSION AIC7XXX_SEQ_VER "$Id: aic7xxx.seq,v 1.34 1996/05/10 16:11:01 gibbs Exp $" @ 1.1 log @Add an Adaptec 2940 driver, by John Aycock and Justin Gibbs, ported to NetBSD by Stefan Grefen, with several bug fixes by me. @ text @d1 82 a82 260 ##+M######################################################################### # Adaptec 274x/284x/294x device driver for Linux and FreeBSD. # # Copyright (c) 1994 John Aycock # The University of Calgary Department of Computer Science. # All rights reserved. # # Modifications/enhancements: # Copyright (c) 1994, 1995 Justin Gibbs. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions, and the following disclaimer. # 2. Redistributions 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. # 3. All advertising materials mentioning features or use of this software # must display the following acknowledgement: # This product includes software developed by the University of Calgary # Department of Computer Science and its contributors. # 4. Neither the name of the University nor the names of its contributors # may be used to endorse or promote products derived from this software # without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF # SUCH DAMAGE. # # FreeBSD, Twin, Wide, 2 command per target support, tagged queuing and other # optimizations provided by Justin T. Gibbs (gibbs@@FreeBSD.org) # ##-M######################################################################### VERSION AIC7XXX_SEQ_VER "$Id: aic7xxx.seq,v 1.15 1995/04/27 17:44:27 gibbs Exp $" SCBMASK = 0x1f SCSISEQ = 0x00 ENRSELI = 0x10 SXFRCTL0 = 0x01 SXFRCTL1 = 0x02 SCSISIGI = 0x03 SCSISIGO = 0x03 SCSIRATE = 0x04 SCSIID = 0x05 SCSIDATL = 0x06 STCNT = 0x08 STCNT+0 = 0x08 STCNT+1 = 0x09 STCNT+2 = 0x0a CLRSINT0 = 0x0b SSTAT0 = 0x0b SELDO = 0x40 SELDI = 0x20 CLRSINT1 = 0x0c SSTAT1 = 0x0c SIMODE1 = 0x11 SCSIBUSL = 0x12 SHADDR = 0x14 SELID = 0x19 SBLKCTL = 0x1f SEQCTL = 0x60 A = 0x64 # == ACCUM SINDEX = 0x65 DINDEX = 0x66 ALLZEROS = 0x6a NONE = 0x6a SINDIR = 0x6c DINDIR = 0x6d FUNCTION1 = 0x6e HADDR = 0x88 HADDR+1 = 0x89 HADDR+2 = 0x8a HADDR+3 = 0x8b HCNT = 0x8c HCNT+0 = 0x8c HCNT+1 = 0x8d HCNT+2 = 0x8e SCBPTR = 0x90 INTSTAT = 0x91 DFCNTRL = 0x93 DFSTATUS = 0x94 DFDAT = 0x99 QINFIFO = 0x9b QINCNT = 0x9c QOUTFIFO = 0x9d SCSICONF_A = 0x5a SCSICONF_B = 0x5b # The two reserved bytes at SCBARRAY+1[23] are expected to be set to # zero, and the reserved bit in SCBARRAY+0 is used as an internal flag # to indicate whether or not to reload scatter-gather parameters after # a disconnect. We also use bits 6 & 7 to indicate whether or not to # initiate SDTR or WDTR repectively when starting this command. # SCBARRAY+0 = 0xa0 DISCONNECTED = 0x04 NEEDDMA = 0x08 SG_LOAD = 0x10 TAG_ENB = 0x20 NEEDSDTR = 0x40 NEEDWDTR = 0x80 SCBARRAY+1 = 0xa1 SCBARRAY+2 = 0xa2 SCBARRAY+3 = 0xa3 SCBARRAY+4 = 0xa4 SCBARRAY+5 = 0xa5 SCBARRAY+6 = 0xa6 SCBARRAY+7 = 0xa7 SCBARRAY+8 = 0xa8 SCBARRAY+9 = 0xa9 SCBARRAY+10 = 0xaa SCBARRAY+11 = 0xab SCBARRAY+12 = 0xac SCBARRAY+13 = 0xad SCBARRAY+14 = 0xae SCBARRAY+15 = 0xaf SCBARRAY+16 = 0xb0 SCBARRAY+17 = 0xb1 SCBARRAY+18 = 0xb2 SCBARRAY+19 = 0xb3 SCBARRAY+20 = 0xb4 SCBARRAY+21 = 0xb5 SCBARRAY+22 = 0xb6 SCBARRAY+23 = 0xb7 SCBARRAY+24 = 0xb8 SCBARRAY+25 = 0xb9 SCBARRAY+26 = 0xba SCBARRAY+27 = 0xbb SCBARRAY+28 = 0xbc SCBARRAY+29 = 0xbd SCBARRAY+30 = 0xbe BAD_PHASE = 0x01 # unknown scsi bus phase CMDCMPLT = 0x02 # Command Complete SEND_REJECT = 0x11 # sending a message reject NO_IDENT = 0x21 # no IDENTIFY after reconnect NO_MATCH = 0x31 # no cmd match for reconnect MSG_SDTR = 0x41 # SDTR message recieved MSG_WDTR = 0x51 # WDTR message recieved MSG_REJECT = 0x61 # Reject message recieved BAD_STATUS = 0x71 # Bad status from target RESIDUAL = 0x81 # Residual byte count != 0 ABORT_TAG = 0x91 # Sent an ABORT_TAG message # The host adapter card (at least the BIOS) uses 20-2f for SCSI # device information, 32-33 and 5a-5f as well. As it turns out, the # BIOS trashes 20-2f, writing the synchronous negotiation results # on top of the BIOS values, so we re-use those for our per-target # scratchspace (actually a value that can be copied directly into # SCSIRATE). The kernel driver will enable synchronous negotiation # for all targets that have a value other than 0 in the lower four # bits of the target scratch space. This should work irregardless of # whether the bios has been installed. NEEDWDTR and NEEDSDTR are the top # two bits of the SCB control byte. The kernel driver will set these # when a WDTR or SDTR message should be sent to the target the SCB's # command references. # # REJBYTE contains the first byte of a MESSAGE IN message, so the driver # can report an intelligible error if a message is rejected. # # FLAGS's high bit is true if we are currently handling a reselect; # its next-highest bit is true ONLY IF we've seen an IDENTIFY message # from the reselecting target. If we haven't had IDENTIFY, then we have # no idea what the lun is, and we can't select the right SCB register # bank, so force a kernel panic if the target attempts a data in/out or # command phase instead of corrupting something. FLAGS also contains # configuration bits so that we can optimize for TWIN and WIDE controllers # as well as the MAX_SYNC bit which we set when we want to negotiate for # 10MHz irregardless of what the per target scratch space says. # # Note that SG_NEXT occupies four bytes. # SYNCNEG = 0x20 REJBYTE = 0x31 DISC_DSB_A = 0x32 DISC_DSB_B = 0x33 MSG_LEN = 0x34 MSG_START+0 = 0x35 MSG_START+1 = 0x36 MSG_START+2 = 0x37 MSG_START+3 = 0x38 MSG_START+4 = 0x39 MSG_START+5 = 0x3a -MSG_START+0 = 0xcb # 2's complement of MSG_START+0 ARG_1 = 0x4a # sdtr conversion args & return BUS_16_BIT = 0x01 RETURN_1 = 0x4a SIGSTATE = 0x4b # value written to SCSISIGO # Linux users should use 0xc (12) for SG_SIZEOF SG_SIZEOF = 0x8 # sizeof(struct ahc_dma) #SG_SIZEOF = 0xc # sizeof(struct scatterlist) SCB_SIZEOF = 0x13 # sizeof SCB to DMA (19 bytes) SG_NOLOAD = 0x4c # load SG pointer/length? SG_COUNT = 0x4d # working value of SG count SG_NEXT = 0x4e # working value of SG pointer SG_NEXT+0 = 0x4e SG_NEXT+1 = 0x4f SG_NEXT+2 = 0x50 SG_NEXT+3 = 0x51 SCBCOUNT = 0x52 # the actual number of SCBs FLAGS = 0x53 # Device configuration flags TWIN_BUS = 0x01 WIDE_BUS = 0x02 MAX_SYNC = 0x08 ACTIVE_MSG = 0x20 IDENTIFY_SEEN = 0x40 RESELECTED = 0x80 ACTIVE_A = 0x54 ACTIVE_B = 0x55 SAVED_TCL = 0x56 # Temporary storage for the # target/channel/lun of a # reconnecting target # After starting the selection hardware, we return to the "poll_for_work" # loop so that we can check for reconnecting targets as well as for our # selection to complete just in case the reselection wins bus arbitration. # The problem with this is that we must keep track of the SCB that we've # already pulled from the QINFIFO and started the selection on just in case # the reselection wins so that we can retry the selection at a later time. # This problem cannot be resolved by holding a single entry in scratch # ram since a reconnecting target can request sense and this will create # yet another SCB waiting for selection. The solution used here is to # use byte 31 of the SCB as a psuedo-next pointer and to thread a list # of SCBs that are awaiting selection. Since 0 is a valid SCB offset, # SCB_LIST_NULL is 0x10 which is out of range. The kernel driver must # add an entry to this list everytime a request sense occurs. The sequencer # will automatically consume the entries. WAITING_SCBH = 0x57 # head of list of SCBs awaiting # selection WAITING_SCBT = 0x58 # tail of list of SCBs awaiting # selection SCB_LIST_NULL = 0x10 # Poll QINCNT for work - the lower bits contain # the number of entries in the Queue In FIFO. # d84 2 a85 1 test WAITING_SCBH,SCB_LIST_NULL jz start_waiting d87 8 a94 4 test FLAGS,TWIN_BUS jz start2 # Are we a twin channel device? # For fairness, we check the other bus first, since we just finished a # transaction on the current channel. xor SBLKCTL,0x08 # Toggle to the other bus d96 1 a96 2 test SSTAT0,SELDO jnz select xor SBLKCTL,0x08 # Toggle to the original bus d99 10 a108 10 test SSTAT0,SELDO jnz select test WAITING_SCBH,SCB_LIST_NULL jz start_waiting test QINCNT,SCBMASK jz poll_for_work # We have at least one queued SCB now and we don't have any # SCBs in the list of SCBs awaiting selection. Set the SCB # pointer from the FIFO so we see the right bank of SCB # registers, then set SCSI options and set the initiator and # target SCSI IDs. # d111 14 a124 35 # If the control byte of this SCB has the NEEDDMA flag set, we have # yet to DMA it from host memory test SCBARRAY+0,NEEDDMA jz test_busy clr HCNT+2 clr HCNT+1 mvi HCNT+0,SCB_SIZEOF mvi DINDEX,HADDR mvi SCBARRAY+26 call bcopy_4 mvi DFCNTRL,0xd # HDMAEN|DIRECTION|FIFORESET # Wait for DMA from host memory to data FIFO to complete, then disable # DMA and wait for it to acknowledge that it's off. # call dma_finish # Copy the SCB from the FIFO to the SCBARRAY mvi DINDEX, SCBARRAY+0 call bcopy_3_dfdat call bcopy_4_dfdat call bcopy_4_dfdat call bcopy_4_dfdat call bcopy_4_dfdat # See if there is not already an active SCB for this target. This code # locks out on a per target basis instead of target/lun. Although this # is not ideal for devices that have multiple luns active at the same # time, it is faster than looping through all SCB's looking for active # commands. It may be benificial to make findscb a more general procedure # to see if the added cost of the search is negligible. This code also # assumes that the kernel driver will clear the active flags on board # initialization, board reset, and a target's SELTO. d127 1 a127 2 test SCBARRAY+0,0x20 jnz start_scb and FUNCTION1,0x70,SCBARRAY+1 d129 1 a129 1 test SCBARRAY+1,0x88 jz test_a # Id < 8 && A channel d132 3 a134 1 or ACTIVE_B,A # Mark the current target as busy d137 1 a137 1 # Place the currently active back on the queue for later processing d142 5 a146 1 # Pull the first entry off of the waiting for selection list d149 1 a149 1 jmp start_scb d152 4 a155 2 test ACTIVE_A,A jnz requeue or ACTIVE_A,A # Mark the current target as busy d158 1 a158 13 and SINDEX,0xf7,SBLKCTL #Clear the channel select bit and A,0x08,SCBARRAY+1 #Get new channel bit or SINDEX,A mov SBLKCTL,SINDEX # select channel mov SCBARRAY+1 call initialize_scsiid # Enable selection phase as an initiator, and do automatic ATN # after the selection. We do this now so that we can overlap the # rest of our work to set up this target with the arbitration and # selection bus phases. # start_selection: or SCSISEQ,0x48 # ENSELO|ENAUTOATNO d160 6 a165 2 clr SG_NOLOAD and FLAGS,0x3f # !RESELECTING d167 8 a174 18 # As soon as we get a successful selection, the target should go # into the message out phase since we have ATN asserted. Prepare # the message to send, locking out the device driver. If the device # driver hasn't beaten us with an ABORT or RESET message, then tack # on an SDTR negotiation if required. # # Messages are stored in scratch RAM starting with a flag byte (high bit # set means active message), one length byte, and then the message itself. # mov SCBARRAY+1 call disconnect # disconnect ok? and SINDEX,0x7,SCBARRAY+1 # lun or SINDEX,A # return value from disconnect or SINDEX,0x80 call mk_mesg # IDENTIFY message mov A,SINDEX test SCBARRAY+0,0xe0 jz !message # WDTR, SDTR or TAG?? cmp MSG_START+0,A jne !message # did driver beat us? d176 32 a207 2 # Tag Message if Tag enabled in SCB control block. Use SCBPTR as the tag # value d210 4 a213 5 mvi DINDEX, MSG_START+1 test SCBARRAY+0,TAG_ENB jz mk_tag_done and A,0x23,SCBARRAY+0 mov DINDIR,A mov DINDIR,SCBPTR d215 1 a215 1 add MSG_LEN,-MSG_START+0,DINDEX # update message length d219 2 a220 1 mov DINDEX call mk_dtr # build DTR message if needed d223 9 a231 6 jmp poll_for_work # Reselection has been initiated by a target. Make a note that we've been # reselected, but haven't seen an IDENTIFY message from the target # yet. # d233 1 d235 2 a236 2 and FLAGS,0x3f # reselected, no IDENTIFY or FLAGS,RESELECTED jmp select2 d238 6 a243 5 # After the selection, remove this SCB from the "waiting for selection" # list. This is achieved by simply moving our "next" pointer into # WAITING_SCBH and setting our next pointer to null so that the next # time this SCB is used, we don't get confused. # d245 2 a246 3 or SCBARRAY+0,NEEDDMA mov WAITING_SCBH,SCBARRAY+30 mvi SCBARRAY+30,SCB_LIST_NULL d248 26 a273 15 call initialize_for_target mvi SCSISEQ,ENRSELI mvi CLRSINT0,0x60 # CLRSELDI|CLRSELDO mvi CLRSINT1,0x8 # CLRBUSFREE # Main loop for information transfer phases. If BSY is false, then # we have a bus free condition, expected or not. Otherwise, wait # for the target to assert REQ before checking MSG, C/D and I/O # for the bus phase. # # We can't simply look at the values of SCSISIGI here (if we want # to do synchronous data transfer), because the target won't assert # REQ if it's already sent us some data that we haven't acknowledged # yet. # d275 2 a276 2 test SSTAT1,0x8 jnz p_busfree # BUSFREE test SSTAT1,0x1 jz ITloop # REQINIT d278 3 a280 1 and A,0xe0,SCSISIGI # CDI|IOI|MSGI d283 5 a287 5 cmp A,0x40 je p_datain cmp A,0x80 je p_command cmp A,0xc0 je p_status cmp A,0xa0 je p_mesgout cmp A,0xe0 je p_mesgin d289 1 a289 1 mvi INTSTAT,BAD_PHASE # unknown - signal driver d292 15 a306 40 mvi 0 call scsisig # !CDO|!IOO|!MSGO call assert call sg_load mvi DINDEX,HADDR mvi SCBARRAY+19 call bcopy_4 # mvi DINDEX,HCNT # implicit since HCNT is next to HADDR mvi SCBARRAY+23 call bcopy_3 mvi DINDEX,STCNT mvi SCBARRAY+23 call bcopy_3 # If we are the last SG block, don't set wideodd. test SCBARRAY+18,0xff jnz p_dataout_wideodd mvi 0x3d call dma # SCSIEN|SDMAEN|HDMAEN| # DIRECTION|FIFORESET jmp p_dataout_rest p_dataout_wideodd: mvi 0xbd call dma # WIDEODD|SCSIEN|SDMAEN|HDMAEN| # DIRECTION|FIFORESET p_dataout_rest: # After a DMA finishes, save the final transfer pointer and count # back into the SCB, in case a device disconnects in the middle of # a transfer. Use SHADDR and STCNT instead of HADDR and HCNT, since # it's a reflection of how many bytes were transferred on the SCSI # (as opposed to the host) bus. # mvi DINDEX,SCBARRAY+23 mvi STCNT call bcopy_3 mvi DINDEX,SCBARRAY+19 mvi SHADDR call bcopy_4 call sg_advance mov SCBARRAY+18,SG_COUNT # residual S/G count jmp ITloop d309 5 a313 1 mvi 0x40 call scsisig # !CDO|IOO|!MSGO a314 1 call sg_load d316 3 a318 2 mvi DINDEX,HADDR mvi SCBARRAY+19 call bcopy_4 d320 15 a334 2 # mvi DINDEX,HCNT # implicit since HCNT is next to HADDR mvi SCBARRAY+23 call bcopy_3 d336 1 a336 14 mvi DINDEX,STCNT mvi SCBARRAY+23 call bcopy_3 # If we are the last SG block, don't set wideodd. test SCBARRAY+18,0xff jnz p_datain_wideodd mvi 0x39 call dma # SCSIEN|SDMAEN|HDMAEN| # !DIRECTION|FIFORESET jmp p_datain_rest p_datain_wideodd: mvi 0xb9 call dma # WIDEODD|SCSIEN|SDMAEN|HDMAEN| # !DIRECTION|FIFORESET p_datain_rest: mvi DINDEX,SCBARRAY+23 mvi STCNT call bcopy_3 d338 33 a370 5 mvi DINDEX,SCBARRAY+19 mvi SHADDR call bcopy_4 call sg_advance mov SCBARRAY+18,SG_COUNT # residual S/G count d372 54 d428 3 a430 4 # Command phase. Set up the DMA registers and let 'er rip - the # two bytes after the SCB SCSI_cmd_length are zeroed by the driver, # so we can copy those three bytes directly into HCNT. # a431 1 mvi 0x80 call scsisig # CDO|!IOO|!MSGO d434 14 a447 8 mvi DINDEX,HADDR mvi SCBARRAY+7 call bcopy_4 # mvi DINDEX,HCNT # implicit since HCNT is next to HADDR mvi SCBARRAY+11 call bcopy_3 mvi DINDEX,STCNT mvi SCBARRAY+11 call bcopy_3 d453 4 a456 3 # Status phase. Wait for the data byte to appear, then read it # and store it into the SCB. # d458 2 a459 1 mvi 0xc0 call scsisig # CDO|IOO|!MSGO d461 4 a464 6 mvi SCBARRAY+14 call inb_first jmp p_mesgin_done # Message out phase. If there is no active message, but the target # took us into this phase anyway, build a no-op message and send it. # d466 2 a467 5 mvi 0xa0 call scsisig # CDO|!IOO|MSGO mvi 0x8 call mk_mesg # build NOP message clr STCNT+2 clr STCNT+1 d469 6 a474 4 # Set up automatic PIO transfer from MSG_START. Bit 3 in # SXFRCTL0 (SPIOEN) is already on. # mvi SINDEX,MSG_START+0 d477 15 a491 32 # When target asks for a byte, drop ATN if it's the last one in # the message. Otherwise, keep going until the message is exhausted. # (We can't use outb for this since it wants the input in SINDEX.) # # Keep an eye out for a phase change, in case the target issues # a MESSAGE REJECT. # p_mesgout2: test SSTAT0,0x2 jz p_mesgout2 # SPIORDY test SSTAT1,0x10 jnz p_mesgout6 # PHASEMIS cmp DINDEX,1 jne p_mesgout3 # last byte? mvi CLRSINT1,0x40 # CLRATNO - drop ATN # Write a byte to the SCSI bus. The AIC-7770 refuses to automatically # send ACKs in automatic PIO or DMA mode unless you make sure that the # "expected" bus phase in SCSISIGO matches the actual bus phase. This # behaviour is completely undocumented and caused me several days of # grief. # # After plugging in different drives to test with and using a longer # SCSI cable, I found that I/O in Automatic PIO mode ceased to function, # especially when transferring >1 byte. It seems to be much more stable # if STCNT is set to one before the transfer, and SDONE (in SSTAT0) is # polled for transfer completion - for both output _and_ input. The # only theory I have is that SPIORDY doesn't drop right away when SCSIDATL # is accessed (like the documentation says it does), and that on a longer # cable run, the sequencer code was fast enough to loop back and see # an SPIORDY that hadn't dropped yet. # p_mesgout3: mvi STCNT+0, 0x01 d493 11 d505 1 a505 4 p_mesgout4: test SSTAT0,0x4 jz p_mesgout4 # SDONE dec DINDEX test DINDEX,0xff jnz p_mesgout2 d507 1 a507 10 # If the next bus phase after ATN drops is a message out, it means # that the target is requesting that the last message(s) be resent. # p_mesgout5: test SSTAT1,0x8 jnz p_mesgout6 # BUSFREE test SSTAT1,0x1 jz p_mesgout5 # REQINIT and A,0xe0,SCSISIGI # CDI|IOI|MSGI cmp A,0xa0 jne p_mesgout6 mvi 0x10 call scsisig # ATNO - re-assert ATN d511 4 a514 3 p_mesgout6: mvi CLRSINT1,0x40 # CLRATNO - in case of PHASEMIS and FLAGS,0xdf # no active msg d517 3 a519 8 # Message in phase. Bytes are read using Automatic PIO mode, but not # using inb. This alleviates a race condition, namely that if ATN had # to be asserted under Automatic PIO mode, it had to beat the SCSI # circuitry sending an ACK to the target. This showed up under heavy # loads and really confused things, since ABORT commands wouldn't be # seen by the drive after an IDENTIFY message in until it had changed # to a data I/O phase. # d521 60 a580 25 mvi 0xe0 call scsisig # CDO|IOO|MSGO mvi A call inb_first # read the 1st message byte mvi REJBYTE,A # save it for the driver cmp ALLZEROS,A jne p_mesgin1 # We got a "command complete" message, so put the SCB pointer # into the Queue Out, and trigger a completion interrupt. # Check status for non zero return and interrupt driver if needed # This allows the driver to interpret errors only when they occur # instead of always uploading the scb. If the status is SCSI_CHECK, # the driver will download a new scb requesting sense to replace # the old one, modify the "waiting for selection" SCB list and set # RETURN_1 to 0x80. If RETURN_1 is set to 0x80 the sequencer imediately # jumps to main loop where it will run down the waiting SCB list. # If the kernel driver does not wish to request sense, it need # only clear RETURN_1, and the command is allowed to complete. We don't # bother to post to the QOUTFIFO in the error case since it would require # extra work in the kernel driver to ensure that the entry was removed # before the command complete code tried processing it. # First check for residuals test SCBARRAY+15,0xff jnz resid test SCBARRAY+16,0xff jnz resid test SCBARRAY+17,0xff jnz resid d583 4 a586 4 test SCBARRAY+14,0xff jz status_ok # 0 Status? mvi INTSTAT,BAD_STATUS # let driver know test RETURN_1, 0x80 jz status_ok jmp p_mesgin_done d589 7 a595 3 # First, mark this target as free. test SCBARRAY+0,0x20 jnz complete # Tagged command and FUNCTION1,0x70,SCBARRAY+1 d597 1 a597 1 test SCBARRAY+1,0x88 jz clear_a d599 1 a599 1 jmp complete d604 9 d614 1 a614 1 mov QOUTFIFO,SCBPTR d616 1 a616 12 jmp p_mesgin_done # If we have a residual count, interrupt and tell the host. Other # alternatives are to pause the sequencer on all command completes (yuck), # dma the resid directly to the host (slick, but a ton of instructions), or # have the sequencer pause itself when it encounters a non-zero resid # (unecessary pause just to flag the command -- yuck, but takes few instructions # and since it shouldn't happen that often is good enough for our purposes). resid: mvi INTSTAT,RESIDUAL jmp check_status a617 11 # Is it an extended message? We only support the synchronous and wide data # transfer request messages, which will probably be in response to # WDTR or SDTR message outs from us. If it's not SDTR or WDTR, reject it - # apparently this can be done after any message in byte, according # to the SCSI-2 spec. # p_mesgin1: cmp A,1 jne p_mesgin2 # extended message code? mvi ARG_1 call inb_next # extended message length mvi A call inb_next # extended message code d619 14 a632 3 cmp A,1 je p_mesginSDTR # Syncronous negotiation message cmp A,3 je p_mesginWDTR # Wide negotiation message jmp p_mesginN d635 15 a649 13 cmp ARG_1,2 jne p_mesginN # extended mesg length = 2 mvi A call inb_next # Width of bus mvi INTSTAT,MSG_WDTR # let driver know test RETURN_1,0x80 jz p_mesgin_done# Do we need to send WDTR? # We didn't initiate the wide negotiation, so we must respond to the request and RETURN_1,0x7f # Clear the SEND_WDTR Flag or FLAGS,ACTIVE_MSG mvi DINDEX,MSG_START+0 mvi MSG_START+0 call mk_wdtr # build WDTR message or SINDEX,0x10,SIGSTATE # turn on ATNO call scsisig jmp p_mesgin_done d652 45 a696 29 cmp ARG_1,3 jne p_mesginN # extended mesg length = 3 mvi ARG_1 call inb_next # xfer period mvi A call inb_next # REQ/ACK offset mvi INTSTAT,MSG_SDTR # call driver to convert test RETURN_1,0xc0 jz p_mesgin_done# Do we need to mk_sdtr or rej? test RETURN_1,0x40 jnz p_mesginN # Requested SDTR too small - rej or FLAGS,ACTIVE_MSG mvi DINDEX, MSG_START+0 mvi MSG_START+0 call mk_sdtr or SINDEX,0x10,SIGSTATE # turn on ATNO call scsisig jmp p_mesgin_done # Is it a disconnect message? Set a flag in the SCB to remind us # and await the bus going free. # p_mesgin2: cmp A,4 jne p_mesgin3 # disconnect code? or SCBARRAY+0,0x4 # set "disconnected" bit jmp p_mesgin_done # Save data pointers message? Copy working values into the SCB, # usually in preparation for a disconnect. # p_mesgin3: cmp A,2 jne p_mesgin4 # save data pointers code? d698 1 a698 1 jmp p_mesgin_done d700 20 a719 6 # Restore pointers message? Data pointers are recopied from the # SCB anyway at the start of any DMA operation, so the only thing # to copy is the scatter-gather values. # p_mesgin4: cmp A,3 jne p_mesgin5 # restore pointers code? d721 1 a721 13 call sg_scb2ram jmp p_mesgin_done # Identify message? For a reconnecting target, this tells us the lun # that the reconnection is for - find the correct SCB and switch to it, # clearing the "disconnected" bit so we don't "find" it by accident later. # p_mesgin5: test A,0x80 jz p_mesgin6 # identify message? test A,0x78 jnz p_mesginN # !DiscPriv|!LUNTAR|!Reserved and A,0x07 # lun in lower three bits d724 1 a724 1 and A,0x08,SBLKCTL # B Channel?? d726 36 a761 2 call inb_last # ACK mov ALLZEROS call findSCB d763 2 a764 5 and SCBARRAY+0,0xfb # clear disconnect bit in SCB or FLAGS,IDENTIFY_SEEN # make note of IDENTIFY call sg_scb2ram # implied restore pointers # required on reselect d766 2 a767 7 get_tag: mvi A call inb_first cmp A,0x20 jne return # Simple Tag message? mvi A call inb_next call inb_last test A,0xf0 jnz abort_tag # Tag in range? mov SCBPTR,A d769 5 a773 3 cmp SCBARRAY+1,A jne abort_tag test SCBARRAY+0,TAG_ENB jz abort_tag ret d775 27 a801 32 or SINDEX,0x10,SIGSTATE # turn on ATNO call scsisig mvi INTSTAT,ABORT_TAG # let driver know mvi 0xd call mk_mesg # ABORT TAG message ret # Message reject? Let the kernel driver handle this. If we have an # outstanding WDTR or SDTR negotiation, assume that it's a response from # the target selecting 8bit or asynchronous transfer, otherwise just ignore # it since we have no clue what it pertains to. # p_mesgin6: cmp A,7 jne p_mesgin7 # message reject code? mvi INTSTAT, MSG_REJECT jmp p_mesgin_done # [ ADD MORE MESSAGE HANDLING HERE ] # p_mesgin7: # We have no idea what this message in is, and there's no way # to pass it up to the kernel, so we issue a message reject and # hope for the best. Since we're now using manual PIO mode to # read in the message, there should no longer be a race condition # present when we assert ATN. In any case, rejection should be a # rare occurrence - signal the driver when it happens. # p_mesginN: or SINDEX,0x10,SIGSTATE # turn on ATNO call scsisig mvi INTSTAT,SEND_REJECT # let driver know d803 5 a807 14 mvi 0x7 call mk_mesg # MESSAGE REJECT message p_mesgin_done: call inb_last # ack & turn auto PIO back on jmp ITloop # Bus free phase. It might be useful to interrupt the device # driver if we aren't expecting this. For now, make sure that # ATN isn't being asserted and look for a new command. # p_busfree: mvi CLRSINT1,0x40 # CLRATNO clr SIGSTATE d810 7 a816 17 # Instead of a generic bcopy routine that requires an argument, we unroll # the two cases that are actually used, and call them explicitly. This # not only reduces the overhead of doing a bcopy by 2/3rds, but ends up # saving space in the program since you don't have to put the argument # into the accumulator before the call. Both functions expect DINDEX to # contain the destination address and SINDEX to contain the source # address. bcopy_3: mov DINDIR,SINDIR mov DINDIR,SINDIR mov DINDIR,SINDIR ret bcopy_4: mov DINDIR,SINDIR mov DINDIR,SINDIR mov DINDIR,SINDIR mov DINDIR,SINDIR ret d818 8 a825 21 bcopy_3_dfdat: mov DINDIR,DFDAT mov DINDIR,DFDAT mov DINDIR,DFDAT ret bcopy_4_dfdat: mov DINDIR,DFDAT mov DINDIR,DFDAT mov DINDIR,DFDAT mov DINDIR,DFDAT ret # Locking the driver out, build a one-byte message passed in SINDEX # if there is no active message already. SINDEX is returned intact. # mk_mesg: mvi SEQCTL,0x50 # PAUSEDIS|FASTMODE test FLAGS,ACTIVE_MSG jnz mk_mesg1 # active message? or FLAGS,ACTIVE_MSG # if not, there is now mvi MSG_LEN,1 # length = 1 mov MSG_START+0,SINDEX # 1-byte message d828 19 a846 1 mvi SEQCTL,0x10 ret # !PAUSEDIS|FASTMODE d848 6 a853 17 # Carefully read data in Automatic PIO mode. I first tried this using # Manual PIO mode, but it gave me continual underrun errors, probably # indicating that I did something wrong, but I feel more secure leaving # Automatic PIO on all the time. # # According to Adaptec's documentation, an ACK is not sent on input from # the target until SCSIDATL is read from. So we wait until SCSIDATL is # latched (the usual way), then read the data byte directly off the bus # using SCSIBUSL. When we have pulled the ATN line, or we just want to # acknowledge the byte, then we do a dummy read from SCISDATL. The SCSI # spec guarantees that the target will hold the data byte on the bus until # we send our ACK. # # The assumption here is that these are called in a particular sequence, # and that REQ is already set when inb_first is called. inb_{first,next} # use the same calling convention as inb. # a854 2 clr STCNT+2 clr STCNT+1 d856 3 a858 1 mov DINDIR,SCSIBUSL ret # read byte directly from bus d860 6 a865 10 inb_next: mov DINDEX,SINDEX # save SINDEX mvi STCNT+0,1 # xfer one byte mov NONE,SCSIDATL # dummy read from latch to ACK inb_next1: test SSTAT0,0x4 jz inb_next1 # SDONE inb_next2: test SSTAT0,0x2 jz inb_next2 # SPIORDY - wait for next byte mov DINDIR,SCSIBUSL ret # read byte directly from bus d867 6 a872 12 inb_last: mvi STCNT+0,1 # ACK with dummy read mov NONE,SCSIDATL inb_last1: test SSTAT0,0x4 jz inb_last1 # wait for completion ret # DMA data transfer. HADDR and HCNT must be loaded first, and # SINDEX should contain the value to load DFCNTRL with - 0x3d for # host->scsi, or 0x39 for scsi->host. The SCSI channel is cleared # during initialization. # d876 11 a886 11 dma2: test SSTAT0,0x1 jnz dma3 # DMADONE test SSTAT1,0x10 jz dma1 # PHASEMIS, ie. underrun # We will be "done" DMAing when the transfer count goes to zero, or # the target changes the phase (in light of this, it makes sense that # the DMA circuitry doesn't ACK when PHASEMIS is active). If we are # doing a SCSI->Host transfer, the data FIFO should be flushed auto- # magically on STCNT=0 or a phase change, so just wait for FIFO empty # status. # d888 1 a888 1 test SINDEX,0x4 jnz dma5 # DIRECTION d890 1 a890 1 test DFSTATUS,0x1 jz dma4 # !FIFOEMP d892 4 a895 5 # Now shut the DMA enables off, and copy STCNT (ie. the underrun # amount, if any) to the SCB registers; SG_COUNT will get copied to # the SCB's residual S/G count field after sg_advance is called. Make # sure that the DMA enables are actually off first lest we get an ILLSADDR. # d897 2 a898 1 clr DFCNTRL # disable DMA d900 1 a900 9 test DFCNTRL,0x38 jnz dma6 # SCSIENACK|SDMAENACK|HDMAENACK mvi DINDEX,SCBARRAY+15 mvi STCNT call bcopy_3 ret dma_finish: test DFSTATUS,0x8 jz dma_finish # HDONE a901 3 clr DFCNTRL # disable DMA dma_finish2: test DFCNTRL,0x8 jnz dma_finish2 # HDMAENACK d904 5 a908 4 # Common SCSI initialization for selection and reselection. Expects # the target SCSI ID to be in the upper four bits of SINDEX, and A's # contents are stomped on return. # d910 1 a910 1 and SINDEX,0xf0 # Get target ID d915 4 a918 24 initialize_for_target: # Turn on Automatic PIO mode now, before we expect to see a REQ # from the target. It shouldn't hurt anything to leave it on. Set # CLRCHN here before the target has entered a data transfer mode - # with synchronous SCSI, if you do it later, you blow away some # data in the SCSI FIFO that the target has already sent to you. # clr SIGSTATE mvi SXFRCTL0,0x8a # DFON|SPIOEN|CLRCHN # Initialize scatter-gather pointers by setting up the working copy # in scratch RAM. # call sg_scb2ram # Initialize SCSIRATE with the appropriate value for this target. # call ndx_dtr mov SCSIRATE,SINDIR ret # Assert that if we've been reselected, then we've seen an IDENTIFY # message. # d920 2 a921 17 test FLAGS,RESELECTED jz return # reselected? test FLAGS,IDENTIFY_SEEN jnz return # seen IDENTIFY? mvi INTSTAT,NO_IDENT ret # no - cause a kernel panic # Find out if disconnection is ok from the information the BIOS has left # us. The tcl from SCBARRAY+1 should be in SINDEX; A will # contain either 0x40 (disconnection ok) or 0x00 (disconnection not ok) # on exit. # # To allow for wide or twin busses, we check the upper bit of the target ID # and the channel ID and look at the appropriate disconnect register. # disconnect: and FUNCTION1,0x70,SINDEX # strip off extra just in case mov A,FUNCTION1 test SINDEX, 0x88 jz disconnect_a d923 1 a923 2 test DISC_DSB_B,A jz disconnect1 # bit nonzero if DISabled clr A ret d925 7 a931 12 disconnect_a: test DISC_DSB_A,A jz disconnect1 # bit nonzero if DISabled clr A ret disconnect1: mvi A,0x40 ret # Locate the SCB matching the target ID/channel/lun in SAVED_TCL and switch # the SCB to it. Have the kernel print a warning message if it can't be # found, and generate an ABORT message to the target. SINDEX should be # cleared on call. # d934 29 a962 5 mov SCBPTR,SINDEX # switch to new SCB cmp SCBARRAY+1,A jne findSCB1 # target ID/channel/lun match? test SCBARRAY+0,0x4 jz findSCB1 # should be disconnected test SCBARRAY+0,TAG_ENB jnz get_tag ret d965 1 d970 9 a978 2 mvi INTSTAT,NO_MATCH # not found - signal kernel mvi 0x6 call mk_mesg # ABORT message d980 3 a982 6 or SINDEX,0x10,SIGSTATE # assert ATNO call scsisig ret # Make a working copy of the scatter-gather parameters in the SCB. # d984 25 a1008 11 mov SG_COUNT,SCBARRAY+2 mvi DINDEX,SG_NEXT mvi SCBARRAY+3 call bcopy_4 mvi SG_NOLOAD,0x80 test SCBARRAY+0,0x10 jnz return # don't reload s/g? clr SG_NOLOAD ret # Copying RAM values back to SCB, for Save Data Pointers message. # d1010 2 a1011 1 mov SCBARRAY+2,SG_COUNT d1013 22 a1034 94 mvi DINDEX,SCBARRAY+3 mvi SG_NEXT call bcopy_4 and SCBARRAY+0,0xef,SCBARRAY+0 test SG_NOLOAD,0x80 jz return # reload s/g? or SCBARRAY+0,SG_LOAD ret # Load a struct scatter if needed and set up the data address and # length. If the working value of the SG count is nonzero, then # we need to load a new set of values. # # This, like the above DMA, assumes a little-endian host data storage. # sg_load: test SG_COUNT,0xff jz return # SG being used? test SG_NOLOAD,0x80 jnz return # don't reload s/g? clr HCNT+2 clr HCNT+1 mvi HCNT+0,SG_SIZEOF mvi DINDEX,HADDR mvi SG_NEXT call bcopy_4 mvi DFCNTRL,0xd # HDMAEN|DIRECTION|FIFORESET # Wait for DMA from host memory to data FIFO to complete, then disable # DMA and wait for it to acknowledge that it's off. # call dma_finish # Copy data from FIFO into SCB data pointer and data count. This assumes # that the struct scatterlist has this structure (this and sizeof(struct # scatterlist) == 12 are asserted in aic7xxx.c): # # struct scatterlist { # char *address; /* four bytes, little-endian order */ # ... /* four bytes, ignored */ # unsigned short length; /* two bytes, little-endian order */ # } # # Not in FreeBSD. the scatter list entry is only 8 bytes. # # struct ahc_dma_seg { # physaddr addr; /* four bytes, little-endian order */ # long len; /* four bytes, little endian order */ # }; # mvi DINDEX, SCBARRAY+19 call bcopy_4_dfdat # For Linux, we must throw away four bytes since there is a 32bit gap # in the middle of a struct scatterlist # mov NONE,DFDAT # mov NONE,DFDAT # mov NONE,DFDAT # mov NONE,DFDAT call bcopy_3_dfdat #Only support 24 bit length. ret # Advance the scatter-gather pointers only IF NEEDED. If SG is enabled, # and the SCSI transfer count is zero (note that this should be called # right after a DMA finishes), then move the working copies of the SG # pointer/length along. If the SCSI transfer count is not zero, then # presumably the target is disconnecting - do not reload the SG values # next time. # sg_advance: test SG_COUNT,0xff jz return # s/g enabled? test STCNT+0,0xff jnz sg_advance1 # SCSI transfer count nonzero? test STCNT+1,0xff jnz sg_advance1 test STCNT+2,0xff jnz sg_advance1 clr SG_NOLOAD # reload s/g next time dec SG_COUNT # one less segment to go clr A # add sizeof(struct scatter) add SG_NEXT+0,SG_SIZEOF,SG_NEXT+0 adc SG_NEXT+1,A,SG_NEXT+1 adc SG_NEXT+2,A,SG_NEXT+2 adc SG_NEXT+3,A,SG_NEXT+3 ret sg_advance1: mvi SG_NOLOAD,0x80 ret # don't reload s/g next time # Add the array base SYNCNEG to the target offset (the target address # is in SCSIID), and return the result in SINDEX. The accumulator # contains the 3->8 decoding of the target ID on return. # d1037 2 a1038 2 test SBLKCTL,0x08 jz ndx_dtr_2 or A,0x08 # Channel B entries add 8 d1040 1 a1040 1 add SINDEX,SYNCNEG,A d1042 7 a1048 9 and FUNCTION1,0x70,SCSIID # 3-bit target address decode mov A,FUNCTION1 ret # If we need to negotiate transfer parameters, build the WDTR or SDTR message # starting at the address passed in SINDEX. DINDEX is modified on return. # The SCSI-II spec requires that Wide negotiation occur first and you can # only negotiat one or the other at a time otherwise in the event of a message # reject, you wouldn't be able to tell which message was the culpret. # d1050 2 a1051 3 test SCBARRAY+0,0xc0 jz return # NEEDWDTR|NEEDSDTR test SCBARRAY+0,NEEDWDTR jnz mk_wdtr_16bit or FLAGS, MAX_SYNC # Force an offset of 15 d1054 3 a1056 3 mvi DINDIR,1 # extended message mvi DINDIR,3 # extended message length = 3 mvi DINDIR,1 # SDTR code d1058 3 a1060 3 mov DINDIR,RETURN_1 # REQ/ACK transfer period test FLAGS, MAX_SYNC jnz mk_sdtr_max_sync and DINDIR,0xf,SINDIR # Sync Offset d1063 1 a1063 1 add MSG_LEN,-MSG_START+0,DINDEX ret # update message length d1065 11 a1075 4 mk_sdtr_max_sync: # We're initiating sync negotiation, so request the max offset we can (15) mvi DINDIR, 0x0f xor FLAGS, MAX_SYNC d1081 4 a1084 4 mvi DINDIR,1 # extended message mvi DINDIR,2 # extended message length = 2 mvi DINDIR,3 # WDTR code mov DINDIR,ARG_1 # bus width d1086 1 a1086 1 add MSG_LEN,-MSG_START+0,DINDEX ret # update message length a1087 11 # Set SCSI bus control signal state. This also saves the last-written # value into a location where the higher-level driver can read it - if # it has to send an ABORT or RESET message, then it needs to know this # so it can assert ATN without upsetting SCSISIGO. The new value is # expected in SINDEX. Change the actual state last to avoid contention # from the driver. # scsisig: mov SIGSTATE,SINDEX mov SCSISIGO,SINDEX ret d1089 1 a1089 1 call ndx_dtr # index scratch space for target d1091 1 a1091 1 dec SINDEX #Preserve SINDEX d1096 1 a1096 1 add RETURN_1,0x18 d1101 3 a1103 2 add RETURN_1,0x18 ret @