diff --git a/distrib/sets/lists/comp/mi b/distrib/sets/lists/comp/mi index 42df3389d6e7..b337dbd13a50 100644 --- a/distrib/sets/lists/comp/mi +++ b/distrib/sets/lists/comp/mi @@ -1,4 +1,4 @@ -# $NetBSD: mi,v 1.307 2001/02/03 02:44:15 jwise Exp $ +# $NetBSD: mi,v 1.308 2001/02/04 17:05:13 ad Exp $ ./sys comp-sysutil-root ./usr/bin/addr2line comp-debug-bin ./usr/bin/ar comp-util-bin @@ -210,6 +210,8 @@ ./usr/include/dev/ic/mc6845reg.h comp-c-include ./usr/include/dev/ic/midwayreg.h comp-c-include ./usr/include/dev/ic/midwayvar.h comp-c-include +./usr/include/dev/ic/mlxio.h comp-c-include +./usr/include/dev/ic/mlxvar.h comp-c-include ./usr/include/dev/ic/ncr5380reg.h comp-c-include ./usr/include/dev/ic/ncr5380var.h comp-c-include ./usr/include/dev/ic/ncr53c400reg.h comp-c-include diff --git a/distrib/sets/lists/man/mi b/distrib/sets/lists/man/mi index cd2738b6111a..554e18303f00 100644 --- a/distrib/sets/lists/man/mi +++ b/distrib/sets/lists/man/mi @@ -1,4 +1,4 @@ -# $NetBSD: mi,v 1.318 2001/02/02 22:01:19 is Exp $ +# $NetBSD: mi,v 1.319 2001/02/04 17:05:14 ad Exp $ ./usr/share/info/am-utils.info man-amd-info ./usr/share/info/as.info man-computil-info ./usr/share/info/awk.info man-util-info @@ -672,6 +672,7 @@ ./usr/share/man/cat4/mii.0 man-sys-catman ./usr/share/man/cat4/mixer.0 man-sys-catman ./usr/share/man/cat4/mk48txx.0 man-sys-catman +./usr/share/man/cat4/mlx.0 man-sys-catman ./usr/share/man/cat4/mpu.0 man-sys-catman ./usr/share/man/cat4/mtio.0 man-sys-catman ./usr/share/man/cat4/music.0 man-sys-catman @@ -2143,6 +2144,7 @@ ./usr/share/man/man4/mii.4 man-sys-man ./usr/share/man/man4/mixer.4 man-sys-man ./usr/share/man/man4/mk48txx.4 man-sys-man +./usr/share/man/man4/mlx.4 man-sys-man ./usr/share/man/man4/mpu.4 man-sys-man ./usr/share/man/man4/mtio.4 man-sys-man ./usr/share/man/man4/music.4 man-sys-man diff --git a/etc/etc.alpha/MAKEDEV b/etc/etc.alpha/MAKEDEV index f313fbc5700c..effa8d919e6c 100644 --- a/etc/etc.alpha/MAKEDEV +++ b/etc/etc.alpha/MAKEDEV @@ -1,5 +1,5 @@ #!/bin/sh - -# $NetBSD: MAKEDEV,v 1.67 2001/01/08 06:21:19 martin Exp $ +# $NetBSD: MAKEDEV,v 1.68 2001/02/04 17:05:14 ad Exp $ # # Copyright (c) 1990 The Regents of the University of California. # All rights reserved. @@ -114,6 +114,7 @@ # ss* SCSI scanner # tun* network tunnel driver # uk* SCSI unknown +# mlx* Mylex DAC960 control interface dialin=0 dialout=524288 @@ -502,6 +503,13 @@ lkm) chmod 640 lkm ;; +mlx*) + unit=${i#mlx}; + rm -f mlx$unit + mknod mlx$unit c 65 $unit + chmod 600 mlx$unit + ;; + wskbd*) unit=${i#wskbd} wskbd=wskbd$unit diff --git a/etc/etc.i386/MAKEDEV b/etc/etc.i386/MAKEDEV index c7d375eae6ee..42641d117cdc 100644 --- a/etc/etc.i386/MAKEDEV +++ b/etc/etc.i386/MAKEDEV @@ -1,6 +1,6 @@ #!/bin/sh - # -# $NetBSD: MAKEDEV,v 1.134 2001/01/08 06:21:19 martin Exp $ +# $NetBSD: MAKEDEV,v 1.135 2001/02/04 17:05:14 ad Exp $ # # Copyright (c) 1990 The Regents of the University of California. # All rights reserved. @@ -121,6 +121,7 @@ # sysmon System Monitoring hardware # bktr Brooktree 848/849/878/879 based TV cards # iop* I2O IOP control interface +# mlx* Mylex DAC960 control interface # dialin=0 @@ -155,7 +156,7 @@ all) sh $0 bpf0 bpf1 bpf2 bpf3 bpf4 bpf5 bpf6 bpf7 sh $0 lpt0 lpt1 lpt2 ttyv0 tun0 tun1 tun2 tun3 ipl sh $0 ccd0 ccd1 ccd2 ccd3 md0 ss0 ch0 uk0 uk1 random - sh $0 speaker lkm mms0 lms0 pms0 joy0 joy1 apm local satlink0 iop0 + sh $0 speaker lkm mms0 lms0 pms0 joy0 joy1 apm local satlink0 iop0 mlx0 sh $0 audio sh $0 usbs sh $0 isdn @@ -776,6 +777,13 @@ iop*) chmod 600 iop$unit ;; +mlx*) + unit=${i#mlx}; + rm -f mlx$unit + mknod mlx$unit c 78 $unit + chmod 600 mlx$unit + ;; + i4b) rm -f i4b mknod i4b c 50 0 diff --git a/share/man/man4/Makefile b/share/man/man4/Makefile index 4d3ccd9fe959..4243cb383c94 100644 --- a/share/man/man4/Makefile +++ b/share/man/man4/Makefile @@ -1,4 +1,4 @@ -# $NetBSD: Makefile,v 1.182 2001/01/31 04:32:18 augustss Exp $ +# $NetBSD: Makefile,v 1.183 2001/02/04 17:05:15 ad Exp $ # @(#)Makefile 8.1 (Berkeley) 6/18/93 MAN= adv.4 adw.4 ahb.4 ahc.4 an.4 aria.4 atalk.4 audio.4 auich.4 auvia.4 \ @@ -9,8 +9,8 @@ MAN= adv.4 adw.4 ahb.4 ahc.4 an.4 aria.4 atalk.4 audio.4 auich.4 auvia.4 \ icsphy.4 idp.4 ifmedia.4 inet.4 inphy.4 intersil7170.4 ioat.4 \ iop.4 iophy.4 iopsp.4 ip.4 ipip.4 ipkdb.4 iso.4 isp.4 \ lc.4 ld.4 lkm.4 lo.4 lxtphy.4 \ - mainbus.4 mbe.4 mca.4 md.4 mhzc.4 midi.4 mii.4 mk48txx.4 mpu.4 \ - mtio.4 ncr.4 ne.4 neo.4 netintro.4 ns.4 nsip.4 \ + mainbus.4 mbe.4 mca.4 md.4 mhzc.4 midi.4 mii.4 mk48txx.4 mlx.4 \ + mpu.4 mtio.4 ncr.4 ne.4 neo.4 netintro.4 ns.4 nsip.4 \ nsphy.4 ntwoc.4 null.4 opl.4 options.4 pas.4 pcdisplay.4 \ pciide.4 pckbc.4 pckbd.4 pcppi.4 pcscp.4 pcweasel.4 pms.4 ppp.4 \ pty.4 puc.4 qsphy.4 raid.4 ray.4 rnd.4 route.4 \ diff --git a/share/man/man4/ld.4 b/share/man/man4/ld.4 index 30b4ca36d355..2c2eb924b718 100644 --- a/share/man/man4/ld.4 +++ b/share/man/man4/ld.4 @@ -1,4 +1,4 @@ -.\" $NetBSD: ld.4,v 1.1 2000/11/26 17:44:12 ad Exp $ +.\" $NetBSD: ld.4,v 1.2 2001/02/04 17:05:15 ad Exp $ .\" .\" Copyright (c) 2000 The NetBSD Foundation, Inc. .\" All rights reserved. @@ -43,6 +43,7 @@ .Sh SYNOPSIS .Cd ld* at cac? unit ? .Cd ld* at iop? tid ? +.Cd ld* at mlx? unit ? .Cd ld* at twe? unit ? .Sh DESCRIPTION The @@ -67,6 +68,7 @@ partition .Xr intro 4 , .Xr cac 4 , .Xr iop 4 , +.Xr mlx 4 , .Xr twe 4 .Sh AUTHOR The @@ -79,8 +81,8 @@ The driver first appeared in .Nx 1.6 . .Sh BUGS -This driver is in reality a nasty hack intended to reduce code size -and maintenace overheads. +This driver is in reality a kludge intended to reduce code size and +maintenace overheads. .Pp The capacity and geometry of units as accessed through the .Nm diff --git a/share/man/man4/mlx.4 b/share/man/man4/mlx.4 new file mode 100644 index 000000000000..32966980b11e --- /dev/null +++ b/share/man/man4/mlx.4 @@ -0,0 +1,100 @@ +.\" $NetBSD: mlx.4,v 1.1 2001/02/04 17:05:15 ad Exp $ +.\" +.\" Copyright (c) 2001 The NetBSD Foundation, Inc. +.\" All rights reserved. +.\" +.\" This code is derived from software contributed to The NetBSD Foundation +.\" by Andrew Doran. +.\" +.\" 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 NetBSD +.\" Foundation, Inc. and its contributors. +.\" 4. Neither the name of The NetBSD Foundation 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +.\" +.Dd January 10, 2000 +.Dt MLX 4 +.Os +.Sh NAME +.Nm mlx +.Nd +.Tn Mylex DAC960 RAID controller driver +.Sh SYNOPSIS +.Cd "mlx* at pci? dev ? function ?" +.Sh DESCRIPTION +The +.Nm +driver provides support for the +.Tn Mylex DAC960 +family of RAID controllers, including OEM versions from +.Tn Compaq +and +.Tn DEC . +Attached disk arrays are supported by the +.Nm ld +driver. +.Pp +The +.Nm +driver will warn if a controller firmware upgrade may be necessary, although +as a matter of course, the latest available firmware should always be used. +.Sh HARDWARE +Supported controllers include: +.Pp +.Bl -tag -width -offset indent -compact +.It Tn DEC/Compaq SWXCR +.It Tn Mylex DAC960P +.It Tn Mylex DAC960PD +.It Tn Mylex DAC960PG +.It Tn Mylex DAC960PJ +.It Tn Mylex DAC960PL +.It Tn Mylex DAC960PR +.It Tn Mylex DAC960PRL +.It Tn Mylex DAC960PT +.It Tn Mylex DAC960PTL0 +.It Tn Mylex DAC960PTL1 +.It Tn Mylex AcceleRAID 150 +.It Tn Mylex AcceleRAID 250 +.It Tn Mylex eXtremeRAID 1100 +.El +.Sh SEE ALSO +.Xr intro 4 , +.Xr ld 4 , +.Xr mlxctl 8 +.Sh AUTHOR +The +.Nm +driver was written by +by Andrew Doran +.Aq ad@netbsd.org . +It is derived from the +.Fx +driver of the same name, written by Micheal Smith +.Aq msmith@freebsd.org . +.Sh HISTORY +The +.Nm mlx +driver first appeared in +.Nx 1.6 . diff --git a/share/man/man4/pci.4 b/share/man/man4/pci.4 index 56387b546178..dab2b3730b94 100644 --- a/share/man/man4/pci.4 +++ b/share/man/man4/pci.4 @@ -1,4 +1,4 @@ -.\" $NetBSD: pci.4,v 1.42 2001/01/22 01:28:54 augustss Exp $ +.\" $NetBSD: pci.4,v 1.43 2001/02/04 17:05:14 ad Exp $ .\" .\" Copyright (c) 1997 Jason R. Thorpe. All rights reserved. .\" Copyright (c) 1997 Jonathan Stone @@ -119,6 +119,8 @@ interfaces. .Bl -tag -width pcdisplay -offset indent .It cac Compaq array controllers. +.It mlx +Mylex DAC960 and DEC SWXCR RAID controllers. .It pciide IDE disk controllers. .It twe @@ -305,6 +307,7 @@ VGA graphics boards. .Xr isp 4 , .Xr le 4 , .Xr lmc 4 , +.Xr mlx 4 , .Xr ncr 4 , .Xr ne 4 , .Xr neo 4 , diff --git a/share/man/man8/man8.alpha/MAKEDEV.8 b/share/man/man8/man8.alpha/MAKEDEV.8 index cdcd76f93986..d6d5471fb054 100644 --- a/share/man/man8/man8.alpha/MAKEDEV.8 +++ b/share/man/man8/man8.alpha/MAKEDEV.8 @@ -6,7 +6,7 @@ .\" *** DO NOT EDIT - any changes will be lost!!! .\" *** ------------------------------------------------------------------ .\" -.\" $NetBSD: MAKEDEV.8,v 1.8 2001/01/08 06:22:42 martin Exp $ +.\" $NetBSD: MAKEDEV.8,v 1.9 2001/02/04 17:05:15 ad Exp $ .\" .\" Copyright (c) 1999 Christopher G. Demetriou .\" All rights reserved. @@ -280,6 +280,9 @@ Network tunnel driver, see . It Ar uk# SCSI unknown, see .Xr uk 4 +. It Ar mlx# +Mylex DAC960 control interface, see +.Xr mlx 4 . El .El .Sh FILES diff --git a/share/man/man8/man8.i386/MAKEDEV.8 b/share/man/man8/man8.i386/MAKEDEV.8 index a2c51adc0346..41877d9ab64d 100644 --- a/share/man/man8/man8.i386/MAKEDEV.8 +++ b/share/man/man8/man8.i386/MAKEDEV.8 @@ -6,7 +6,7 @@ .\" *** DO NOT EDIT - any changes will be lost!!! .\" *** ------------------------------------------------------------------ .\" -.\" $NetBSD: MAKEDEV.8,v 1.17 2001/01/08 06:22:43 martin Exp $ +.\" $NetBSD: MAKEDEV.8,v 1.18 2001/02/04 17:05:15 ad Exp $ .\" .\" Copyright (c) 1991, 1993 .\" The Regents of the University of California. All rights reserved. @@ -285,6 +285,9 @@ Brooktree 848/849/878/879 based TV cards, see . It Ar iop# I2O IOP control interface, see .Xr iop 4 +. It Ar mlx# +Mylex DAC960 control interface, see +.Xr mlx 4 . El .El .Pp diff --git a/sys/arch/alpha/alpha/conf.c b/sys/arch/alpha/alpha/conf.c index 881d3fd7d75d..7c6be82aa474 100644 --- a/sys/arch/alpha/alpha/conf.c +++ b/sys/arch/alpha/alpha/conf.c @@ -1,4 +1,4 @@ -/* $NetBSD: conf.c,v 1.55 2001/01/14 11:17:28 martin Exp $ */ +/* $NetBSD: conf.c,v 1.56 2001/02/04 17:05:13 ad Exp $ */ /*- * Copyright (c) 1991 The Regents of the University of California. @@ -37,7 +37,7 @@ #include /* RCS ID & Copyright macro defns */ -__KERNEL_RCSID(0, "$NetBSD: conf.c,v 1.55 2001/01/14 11:17:28 martin Exp $"); +__KERNEL_RCSID(0, "$NetBSD: conf.c,v 1.56 2001/02/04 17:05:13 ad Exp $"); #include #include @@ -161,6 +161,8 @@ cdev_decl(satlink); cdev_decl(midi); #include "sequencer.h" cdev_decl(music); +#include "mlx.h" +cdev_decl(mlx); #include "a12dc.h" #include "scc.h" @@ -332,6 +334,7 @@ struct cdevsw cdevsw[] = cdev_usbdev_init(NURIO,urio), /* 62: Diamond Rio 500 */ cdev_ugen_init(NUSCANNER,uscanner),/* 63: USB scanner */ cdev_altq_init(NALTQ,altq), /* 64: ALTQ control interface */ + cdev__oci_init(NMLX,mlx), /* 65: Mylex DAC960 control interface */ }; int nchrdev = sizeof (cdevsw) / sizeof (cdevsw[0]); @@ -438,6 +441,7 @@ static int chrtoblktbl[] = { /* 62 */ NODEV, /* 63 */ NODEV, /* 64 */ NODEV, + /* 65 */ NODEV, }; /* diff --git a/sys/arch/alpha/conf/ALPHA b/sys/arch/alpha/conf/ALPHA index 4c285f2d099f..a211bc663b90 100644 --- a/sys/arch/alpha/conf/ALPHA +++ b/sys/arch/alpha/conf/ALPHA @@ -1,10 +1,10 @@ -# $NetBSD: ALPHA,v 1.144 2001/01/27 03:02:52 lukem Exp $ +# $NetBSD: ALPHA,v 1.145 2001/02/04 17:05:16 ad Exp $ # # Alpha kernel with all the options you'd want, and more. include "arch/alpha/conf/std.alpha" -ident "ALPHA-$Revision: 1.144 $" +ident "ALPHA-$Revision: 1.145 $" maxusers 32 @@ -252,6 +252,7 @@ isp* at pci? dev ? function ? # Qlogic ISP 10x0 SCSI le* at pci? dev ? function ? # PCI LANCE Ethernet (untested) #ncr* at pci? dev ? function ? # NCR 53c8xx SCSI siop* at pci? dev ? function ? # Symbios 53c8xx SCSI +mlx* at pci? dev ? function ? # Mylex DAC960 / DEC SWXCR (untested) ne* at pci? dev ? function ? # NE2000-compatible Ethernet ohci* at pci? dev ? function ? # USB Open Host Controller pceb* at pci? dev ? function ? # Intel PCI-EISA Bridges @@ -419,6 +420,7 @@ fd* at fdc? drive ? # Hardware RAID devices ld* at cac? unit ? +ld* at mlx? unit ? # USB bus support usb* at uhci? diff --git a/sys/arch/alpha/conf/GENERIC b/sys/arch/alpha/conf/GENERIC index db47f546bcf7..1462db34d237 100644 --- a/sys/arch/alpha/conf/GENERIC +++ b/sys/arch/alpha/conf/GENERIC @@ -1,4 +1,4 @@ -# $NetBSD: GENERIC,v 1.180 2001/01/27 03:02:53 lukem Exp $ +# $NetBSD: GENERIC,v 1.181 2001/02/04 17:05:16 ad Exp $ # # Generic Alpha kernel. Enough to get booted, etc., but not much more. # @@ -6,7 +6,7 @@ include "arch/alpha/conf/std.alpha" -#ident "GENERIC-$Revision: 1.180 $" +#ident "GENERIC-$Revision: 1.181 $" maxusers 32 @@ -225,6 +225,7 @@ isp* at pci? dev ? function ? # Qlogic ISP 10x0 SCSI le* at pci? dev ? function ? # PCI LANCE Ethernet (untested) #ncr* at pci? dev ? function ? # NCR 53c8xx SCSI siop* at pci? dev ? function ? # Symbios 53c8xx SCSI +mlx* at pci? dev ? function ? # Mylex DAC960 / DEC SWXCR (untested) ne* at pci? dev ? function ? # NE2000-compatible Ethernet ohci* at pci? dev ? function ? # USB Open Host Controller pceb* at pci? dev ? function ? # Intel PCI-EISA Bridges @@ -383,6 +384,7 @@ fd* at fdc? drive ? # Hardware RAID devices ld* at cac? unit ? +ld* at mlx? unix ? # USB bus support usb* at uhci? diff --git a/sys/arch/alpha/conf/INSTALL b/sys/arch/alpha/conf/INSTALL index 8934ee038bbc..90ab257a6d37 100644 --- a/sys/arch/alpha/conf/INSTALL +++ b/sys/arch/alpha/conf/INSTALL @@ -1,4 +1,4 @@ -# $NetBSD: INSTALL,v 1.49 2000/12/21 23:05:47 thorpej Exp $ +# $NetBSD: INSTALL,v 1.50 2001/02/04 17:05:16 ad Exp $ # # Alpha INSTALL kernel. @@ -177,6 +177,7 @@ isp* at pci? dev ? function ? # Qlogic ISP 10x0 SCSI le* at pci? dev ? function ? # PCI LANCE Ethernet (untested) #ncr* at pci? dev ? function ? # NCR 53c8xx SCSI siop* at pci? dev ? function ? # Symbios 53c8xx SCSI +mlx* at pci? dev ? function ? # Mylex DAC960 / DEC SWXCR (untested) ne* at pci? dev ? function ? # NE2000-compatible Ethernet pceb* at pci? dev ? function ? # Intel PCI-EISA Bridges pciide* at pci? dev ? function ? # PCI IDE controllers @@ -280,6 +281,7 @@ fd* at fdc? drive ? # Hardware RAID devices ld* at cac? unit ? +ld* at mlx? unix ? # Workstation Console attachments wsdisplay* at vga? diff --git a/sys/arch/i386/conf/GENERIC b/sys/arch/i386/conf/GENERIC index 678e2a171ddf..43bd34fec2cb 100644 --- a/sys/arch/i386/conf/GENERIC +++ b/sys/arch/i386/conf/GENERIC @@ -1,11 +1,11 @@ -# $NetBSD: GENERIC,v 1.398 2001/02/02 10:53:48 fvdl Exp $ +# $NetBSD: GENERIC,v 1.399 2001/02/04 17:05:15 ad Exp $ # # GENERIC -- everything that's currently supported # include "arch/i386/conf/std.i386" -#ident "GENERIC-$Revision: 1.398 $" +#ident "GENERIC-$Revision: 1.399 $" maxusers 32 # estimated number of users @@ -473,10 +473,12 @@ uk* at scsibus? target ? lun ? # SCSI unknown # RAID controllers and devices cac* at eisa? # Compaq EISA array controllers cac* at pci? dev ? function ? # Compaq PCI array controllers +mlx* at pci? dev ? function ? # Mylex DAC960 & DEC SWXCR family twe* at pci? dev ? function ? # 3ware Escalade RAID controllers -ld* at cac? unit ? # Compaq array disk devices -ld* at twe? unit ? # 3ware array disk devices +ld* at cac? unit ? # logical disk devices +ld* at twe? unit ? +ld* at mlx? unit ? # IDE and related devices # PCI IDE controllers - see pciide(4) for supported hardware. diff --git a/sys/arch/i386/conf/INSTALL b/sys/arch/i386/conf/INSTALL index 20ca9342718a..bb460142ef1e 100644 --- a/sys/arch/i386/conf/INSTALL +++ b/sys/arch/i386/conf/INSTALL @@ -1,4 +1,4 @@ -# $NetBSD: INSTALL,v 1.162 2001/01/16 03:38:23 augustss Exp $ +# $NetBSD: INSTALL,v 1.163 2001/02/04 17:05:15 ad Exp $ # # INSTALL - Installation kernel. # @@ -366,10 +366,12 @@ cd* at scsibus? target ? lun ? # SCSI CD-ROM drives # RAID controllers and devices cac* at eisa? # Compaq EISA array controllers cac* at pci? dev ? function ? # Compaq PCI array controllers +mlx* at pci? dev ? function ? # Mylex DAC960 & DEC SWXCR family twe* at pci? dev ? function ? # 3ware Escalade RAID controllers -ld* at cac? unit ? # Compaq array disk devices -ld* at twe? unit ? # 3ware array disk devices +ld* at cac? unit ? # logical disk devices +ld* at twe? unit ? +ld* at mlx? unit ? # IDE and related devices # PCI IDE controllers - see pciide(4) for supported hardware. diff --git a/sys/arch/i386/conf/INSTALL_SMALL b/sys/arch/i386/conf/INSTALL_SMALL index 37970eb65a71..d9d22d34ff2b 100644 --- a/sys/arch/i386/conf/INSTALL_SMALL +++ b/sys/arch/i386/conf/INSTALL_SMALL @@ -1,4 +1,4 @@ -# $NetBSD: INSTALL_SMALL,v 1.63 2001/01/24 18:08:26 jmc Exp $ +# $NetBSD: INSTALL_SMALL,v 1.64 2001/02/04 17:05:15 ad Exp $ # # INSTALL_SMALL - Small Installation kernel. # @@ -322,9 +322,12 @@ cd* at scsibus? target ? lun ? # SCSI CD-ROM drives # RAID controllers and devices #cac* at eisa? # Compaq EISA array controllers #cac* at pci? dev ? function ? # Compaq PCI array controllers +#mlx* at pci? dev ? function ? # Mylex DAC960 & DEC SWXCR family #twe* at pci? dev ? function ? # 3ware Escalade RAID controllers -#ld* at cac? unit ? # Compaq array disk devices -#ld* at twe? unit ? # 3ware array disk devices + +#ld* at cac? unit ? # logical disk devices +#ld* at twe? unit ? +#ld* at mlx? unit ? # IDE and related devices diff --git a/sys/arch/i386/conf/INSTALL_TINY b/sys/arch/i386/conf/INSTALL_TINY index fa7ff410d6ae..ccb0a51912a0 100644 --- a/sys/arch/i386/conf/INSTALL_TINY +++ b/sys/arch/i386/conf/INSTALL_TINY @@ -1,4 +1,4 @@ -# $NetBSD: INSTALL_TINY,v 1.31 2000/11/26 17:44:09 ad Exp $ +# $NetBSD: INSTALL_TINY,v 1.32 2001/02/04 17:05:16 ad Exp $ # # INSTALL_TINY - Tiny Installation kernel, suitable for 4M machines. # @@ -323,7 +323,12 @@ pc0 at isa? port 0x60 irq 1 # pccons generic PC console driver # RAID controllers and devices #cac* at eisa? # Compaq EISA array controllers #cac* at pci? dev ? function ? # Compaq PCI array controllers -#ca* at cac? unit ? # Compaq array disk devices +#mlx* at pci? dev ? function ? # Mylex DAC960 & DEC SWXCR family +#twe* at pci? dev ? function ? # 3ware Escalade RAID controllers + +#ld* at cac? unit ? # logical disk devices +#ld* at twe? unit ? +#ld* at mlx? unit ? # IDE and related devices diff --git a/sys/arch/i386/i386/conf.c b/sys/arch/i386/i386/conf.c index 170b63741a4a..0c5c0b3d8f68 100644 --- a/sys/arch/i386/i386/conf.c +++ b/sys/arch/i386/i386/conf.c @@ -1,4 +1,4 @@ -/* $NetBSD: conf.c,v 1.137 2001/01/14 11:17:30 martin Exp $ */ +/* $NetBSD: conf.c,v 1.138 2001/02/04 17:05:13 ad Exp $ */ /*- * Copyright (c) 1998 The NetBSD Foundation, Inc. @@ -298,6 +298,8 @@ cdev_decl(i4btel); cdev_decl(vmegeneric); #include "iop.h" cdev_decl(iop); +#include "mlx.h" +cdev_decl(mlx); #include @@ -388,6 +390,7 @@ struct cdevsw cdevsw[] = cdev_ugen_init(NUSCANNER,uscanner),/* 75: USB scanner */ cdev__oci_init(NIOP,iop), /* 76: I2O IOP control interface */ cdev_altq_init(NALTQ,altq), /* 77: ALTQ control interface */ + cdev__oci_init(NMLX,mlx), /* 78: Mylex DAC960 control interface */ }; int nchrdev = sizeof(cdevsw) / sizeof(cdevsw[0]); @@ -507,6 +510,7 @@ static int chrtoblktbl[] = { /* 75 */ NODEV, /* 76 */ NODEV, /* 77 */ NODEV, + /* 78 */ NODEV, }; /* diff --git a/sys/conf/files b/sys/conf/files index 13356e8ee682..964218a8f6e5 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -1,4 +1,4 @@ -# $NetBSD: files,v 1.418 2001/02/02 04:39:35 tv Exp $ +# $NetBSD: files,v 1.419 2001/02/04 17:05:11 ad Exp $ # @(#)files.newconf 7.5 (Berkeley) 5/10/93 @@ -244,6 +244,13 @@ file dev/ic/cac.c cac attach ld at cac with ld_cac file dev/ic/ld_cac.c ld_cac +# Mylex DAC960 RAID controllers +device mlx {unit = -1} +file dev/ic/mlx.c mlx needs-flag + +attach ld at mlx with ld_mlx +file dev/ic/ld_mlx.c ld_mlx + # AdvanSys 1200A, 1200B and ULTRA SCSI controllers device adv: scsi file dev/ic/adv.c adv diff --git a/sys/dev/ic/Makefile b/sys/dev/ic/Makefile index 2478991266d6..b54a57d74875 100644 --- a/sys/dev/ic/Makefile +++ b/sys/dev/ic/Makefile @@ -1,4 +1,4 @@ -# $NetBSD: Makefile,v 1.12 2000/06/09 05:31:15 onoe Exp $ +# $NetBSD: Makefile,v 1.13 2001/02/04 17:05:12 ad Exp $ INCSDIR= /usr/include/dev/ic @@ -13,9 +13,9 @@ INCS= ad1848reg.h ahareg.h ahavar.h aic6360reg.h aic6360var.h \ i82595reg.h ics2101reg.h ims332reg.h intersil7170.h interwavereg.h \ interwavevar.h ispmbox.h ispreg.h ispvar.h lemacreg.h lemacvar.h \ lptreg.h lptvar.h mb86960reg.h mb86960var.h mc146818reg.h \ - mc68450reg.h mc6845reg.h midwayreg.h midwayvar.h ncr5380reg.h \ - ncr5380var.h ncr53c400reg.h ncr53c9xreg.h ncr53c9xvar.h ne2000reg.h \ - ne2000var.h \ + mc68450reg.h mc6845reg.h midwayreg.h midwayvar.h mlxio.h mlxreg.h \ + ncr5380reg.h ncr5380var.h ncr53c400reg.h ncr53c9xreg.h ncr53c9xvar.h \ + ne2000reg.h ne2000var.h \ nec765reg.h ns16450reg.h ns16550reg.h opl3sa3reg.h pcdisplay.h \ pcdisplayvar.h pckbcvar.h pdqreg.h pdqvar.h rrunnerreg.h rrunnervar.h \ smc83c170reg.h smc83c170var.h smc90cx6reg.h smc91cxxreg.h \ diff --git a/sys/dev/ic/ld_mlx.c b/sys/dev/ic/ld_mlx.c new file mode 100644 index 000000000000..fea9c88e8830 --- /dev/null +++ b/sys/dev/ic/ld_mlx.c @@ -0,0 +1,278 @@ +/* $NetBSD: ld_mlx.c,v 1.1 2001/02/04 17:05:12 ad Exp $ */ + +/*- + * Copyright (c) 2001 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Andrew Doran. + * + * 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 NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + +/* + * Mylex DAC960 front-end for ld(4) driver. + */ + +#include "rnd.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#if NRND > 0 +#include +#endif + +#include +#include + +#include + +#include +#include +#include + +struct ld_mlx_softc { + struct ld_softc sc_ld; + int sc_hwunit; +}; + +static void ld_mlx_attach(struct device *, struct device *, void *); +static int ld_mlx_detach(struct device *, int); +static int ld_mlx_dobio(struct ld_mlx_softc *, void *, int, int, int, + struct buf *); +static int ld_mlx_dump(struct ld_softc *, void *, int, int); +static void ld_mlx_handler(struct mlx_ccb *); +static int ld_mlx_match(struct device *, struct cfdata *, void *); +static int ld_mlx_start(struct ld_softc *, struct buf *); + +struct cfattach ld_mlx_ca = { + sizeof(struct ld_mlx_softc), + ld_mlx_match, + ld_mlx_attach, + ld_mlx_detach +}; + +static int +ld_mlx_match(struct device *parent, struct cfdata *match, void *aux) +{ + + return (1); +} + +static void +ld_mlx_attach(struct device *parent, struct device *self, void *aux) +{ + struct mlx_attach_args *mlxa; + struct ld_mlx_softc *sc; + struct ld_softc *ld; + struct mlx_softc *mlx; + struct mlx_sysdrive *ms; + const char *statestr; + + sc = (struct ld_mlx_softc *)self; + ld = &sc->sc_ld; + mlx = (struct mlx_softc *)parent; + mlxa = aux; + ms = &mlx->mlx_sysdrive[mlxa->mlxa_unit]; + + sc->sc_hwunit = mlxa->mlxa_unit; + ld->sc_maxxfer = MLX_MAX_XFER; + ld->sc_secsize = MLX_SECTOR_SIZE; + ld->sc_maxqueuecnt = 1; + ld->sc_start = ld_mlx_start; + ld->sc_dump = ld_mlx_dump; + + /* + * Build synthetic geometry. + */ + ld->sc_secperunit = ms->ms_size; + + if (ld->sc_secperunit > 0x200000) { + ld->sc_nheads = 255; + ld->sc_nsectors = 63; + ld->sc_ncylinders = ms->ms_size / (255 * 63); + } else { + ld->sc_nheads = 128; + ld->sc_nsectors = 32; + ld->sc_ncylinders = ms->ms_size / (128 * 32); + } + + /* + * Report on current status, and attach to the ld driver proper. + */ + switch (ms->ms_state) { + case MLX_SYSD_ONLINE: + statestr = "online"; + ld->sc_flags = LDF_ENABLED; + break; + + case MLX_SYSD_CRITICAL: + statestr = "critical"; + ld->sc_flags = LDF_ENABLED; + break; + + case MLX_SYSD_OFFLINE: + statestr = "offline"; + break; + + default: + statestr = "state unknown"; + break; + } + + if (ms->ms_raidlevel == MLX_SYS_DRV_JBOD) + printf(": JBOD, %s\n", statestr); + else + printf(": RAID%d, %s\n", ms->ms_raidlevel, statestr); + + ldattach(ld); +} + +static int +ld_mlx_detach(struct device *dv, int flags) +{ + int rv; + + if ((rv = ldbegindetach((struct ld_softc *)dv, flags)) != 0) + return (rv); + ldenddetach((struct ld_softc *)dv); + mlx_flush((struct mlx_softc *)dv->dv_parent, 1); + + return (0); +} + +static int +ld_mlx_dobio(struct ld_mlx_softc *sc, void *data, int datasize, int blkno, + int dowrite, struct buf *bp) +{ + struct mlx_ccb *mc; + struct mlx_softc *mlx; + int s, rv; + bus_addr_t sgphys; + + mlx = (struct mlx_softc *)sc->sc_ld.sc_dv.dv_parent; + + if ((rv = mlx_ccb_alloc(mlx, &mc, bp == NULL)) != 0) + return (rv); + + /* Map the data transfer. */ + rv = mlx_ccb_map(mlx, mc, data, datasize, + dowrite ? MC_XFER_OUT : MC_XFER_IN); + if (rv != 0) { + mlx_ccb_free(mlx, mc); + return (rv); + } + + /* Build the command. */ + sgphys = mlx->mlx_sgls_paddr + (MLX_SGL_SIZE * mc->mc_ident); + datasize /= MLX_SECTOR_SIZE; + + if (mlx->mlx_iftype == 2) + mlx_make_type1(mc, + dowrite ? MLX_CMD_WRITESG_OLD : MLX_CMD_READSG_OLD, + datasize & 0xff, blkno, sc->sc_hwunit, sgphys, + mc->mc_nsgent); + else + mlx_make_type5(mc, + dowrite ? MLX_CMD_WRITESG : MLX_CMD_READSG, + datasize & 0xff, + (sc->sc_hwunit << 3) | ((datasize >> 8) & 0x07), + blkno, sgphys, mc->mc_nsgent); + + if (bp == NULL) { + s = splbio(); + rv = mlx_ccb_poll(mlx, mc, 10000); + mlx_ccb_unmap(mlx, mc); + mlx_ccb_free(mlx, mc); + splx(s); + } else { + mc->mc_mx.mx_handler = ld_mlx_handler; + mc->mc_mx.mx_context = bp; + mc->mc_mx.mx_dv = &sc->sc_ld.sc_dv; + mlx_ccb_enqueue(mlx, mc); + rv = 0; + } + + return (rv); +} + +static int +ld_mlx_start(struct ld_softc *ld, struct buf *bp) +{ + + return (ld_mlx_dobio((struct ld_mlx_softc *)ld, bp->b_data, + bp->b_bcount, bp->b_rawblkno, (bp->b_flags & B_READ) == 0, bp)); +} + +static void +ld_mlx_handler(struct mlx_ccb *mc) +{ + struct buf *bp; + struct mlx_context *mx; + struct ld_mlx_softc *sc; + struct mlx_softc *mlx; + + mx = &mc->mc_mx; + bp = mx->mx_context; + sc = (struct ld_mlx_softc *)mx->mx_dv; + mlx = (struct mlx_softc *)sc->sc_ld.sc_dv.dv_parent; + + if (mc->mc_status != MLX_STATUS_OK) { + bp->b_flags |= B_ERROR; + bp->b_error = EIO; + bp->b_resid = bp->b_bcount; + + if (mc->mc_status == MLX_STATUS_RDWROFFLINE) + printf("%s: drive offline\n", + sc->sc_ld.sc_dv.dv_xname); + else + printf("%s: I/O error - %s\n", + sc->sc_ld.sc_dv.dv_xname, + mlx_ccb_diagnose(mc)); + } else + bp->b_resid = 0; + + mlx_ccb_unmap(mlx, mc); + mlx_ccb_free(mlx, mc); + lddone(&sc->sc_ld, bp); +} + +static int +ld_mlx_dump(struct ld_softc *ld, void *data, int blkno, int blkcnt) +{ + + return (ld_mlx_dobio((struct ld_mlx_softc *)ld, data, + blkcnt * ld->sc_secsize, blkno, 1, NULL)); +} diff --git a/sys/dev/ic/mlx.c b/sys/dev/ic/mlx.c new file mode 100644 index 000000000000..706af90f0278 --- /dev/null +++ b/sys/dev/ic/mlx.c @@ -0,0 +1,2174 @@ +/* $NetBSD: mlx.c,v 1.1 2001/02/04 17:05:12 ad Exp $ */ + +/*- + * Copyright (c) 2001 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Andrew Doran. + * + * 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 NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + +/*- + * Copyright (c) 1999 Michael Smith + * 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. + * + * 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. + * + * from FreeBSD: mlx.c,v 1.14.2.3 2000/08/04 06:52:50 msmith Exp + */ + +/* + * Driver for the Mylex DAC960 family of RAID controllers. + * + * TODO: + * + * o Homogenize return values everywhere. + * o Verify that status messages are correct. + * o Test and enable channel pause. + * o Time out commands on software queue. + * o Don't use a S/G list for single-segment transfers. + * o SCSI pass-through. + */ + +#include "ld.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include +#include +#include + +#define MLX_TIMEOUT 60 + +#ifdef DIAGNOSTIC +#define DPRINTF(x) printf x +#else +#define DPRINTF(x) +#endif + +static void mlx_adjqparam(struct mlx_softc *, int, int); +static int mlx_ccb_submit(struct mlx_softc *, struct mlx_ccb *); +static int mlx_check(struct mlx_softc *, int); +static void mlx_configure(struct mlx_softc *, int); +static void mlx_describe(struct mlx_softc *); +static void *mlx_enquire(struct mlx_softc *, int, size_t, + void (*)(struct mlx_ccb *), int); +static int mlx_fw_message(struct mlx_softc *, int, int, int); +static void mlx_pause_action(struct mlx_softc *); +static void mlx_pause_done(struct mlx_ccb *); +static void mlx_periodic(struct mlx_softc *); +static void mlx_periodic_create(void *); +static void mlx_periodic_enquiry(struct mlx_ccb *); +static void mlx_periodic_eventlog_poll(struct mlx_softc *); +static void mlx_periodic_eventlog_respond(struct mlx_ccb *); +static void mlx_periodic_rebuild(struct mlx_ccb *); +static void mlx_periodic_thread(void *); +static int mlx_print(void *, const char *); +static int mlx_rebuild(struct mlx_softc *, int, int); +static void mlx_shutdown(void *); +static int mlx_submatch(struct device *, struct cfdata *, void *); +static int mlx_user_command(struct mlx_softc *, struct mlx_usercommand *); + +static __inline__ time_t mlx_curtime(void); + +cdev_decl(mlx); + +extern struct cfdriver mlx_cd; +static struct proc *mlx_periodic_proc; +static void *mlx_sdh; + +struct { + int hwid; + const char *name; +} static const mlx_cname[] = { + { 0x01, "960P/PD" }, + { 0x02, "960PL" }, + { 0x10, "960PG" }, + { 0x11, "960PJ" }, + { 0x12, "960PR" }, + { 0x13, "960PT" }, + { 0x14, "960PTL0" }, + { 0x15, "960PRL" }, + { 0x16, "960PTL1" }, + { 0x20, "1164PVX" }, +}; + +static const char * const mlx_sense_msgs[] = { + "because write recovery failed", + "because of SCSI bus reset failure", + "because of double check condition", + "because it was removed", + "because of gross error on SCSI chip", + "because of bad tag returned from drive", + "because of timeout on SCSI command", + "because of reset SCSI command issued from system", + "because busy or parity error count exceeded limit", + "because of 'kill drive' command from system", + "because of selection timeout", + "due to SCSI phase sequence error", + "due to unknown status" +}; + +static const char * const mlx_status_msgs[] = { + "normal completion", /* 0 */ + "irrecoverable data error", /* 1 */ + "drive does not exist, or is offline", /* 2 */ + "attempt to write beyond end of drive", /* 3 */ + "bad data encountered", /* 4 */ + "invalid log entry request", /* 5 */ + "attempt to rebuild online drive", /* 6 */ + "new disk failed during rebuild", /* 7 */ + "invalid channel/target", /* 8 */ + "rebuild/check already in progress", /* 9 */ + "one or more disks are dead", /* 10 */ + "invalid or non-redundant drive", /* 11 */ + "channel is busy", /* 12 */ + "channel is not stopped", /* 13 */ + "rebuild successfully terminated", /* 14 */ + "unsupported command", /* 15 */ + "check condition received", /* 16 */ + "device is busy", /* 17 */ + "selection or command timeout", /* 18 */ + "command terminated abnormally", /* 19 */ + "controller wedged", /* 20 */ + "software timeout", /* 21 */ + "command busy (???)", /* 22 */ +}; + +struct { + u_char command; + u_char msg; /* Index into mlx_status_msgs[]. */ + u_short status; +} static const mlx_msgs[] = { + { MLX_CMD_READSG, 1, 0x0001 }, + { MLX_CMD_READSG, 1, 0x0002 }, + { MLX_CMD_READSG, 3, 0x0105 }, + { MLX_CMD_READSG, 4, 0x010c }, + { MLX_CMD_WRITESG, 1, 0x0001 }, + { MLX_CMD_WRITESG, 1, 0x0002 }, + { MLX_CMD_WRITESG, 3, 0x0105 }, + { MLX_CMD_READSG_OLD, 1, 0x0001 }, + { MLX_CMD_READSG_OLD, 1, 0x0002 }, + { MLX_CMD_READSG_OLD, 3, 0x0105 }, + { MLX_CMD_WRITESG_OLD, 1, 0x0001 }, + { MLX_CMD_WRITESG_OLD, 1, 0x0002 }, + { MLX_CMD_WRITESG_OLD, 3, 0x0105 }, + { MLX_CMD_LOGOP, 5, 0x0105 }, + { MLX_CMD_REBUILDASYNC, 6, 0x0002 }, + { MLX_CMD_REBUILDASYNC, 7, 0x0004 }, + { MLX_CMD_REBUILDASYNC, 8, 0x0105 }, + { MLX_CMD_REBUILDASYNC, 9, 0x0106 }, + { MLX_CMD_REBUILDASYNC, 14, 0x0107 }, + { MLX_CMD_CHECKASYNC, 10, 0x0002 }, + { MLX_CMD_CHECKASYNC, 11, 0x0105 }, + { MLX_CMD_CHECKASYNC, 9, 0x0106 }, + { MLX_CMD_STOPCHANNEL, 12, 0x0106 }, + { MLX_CMD_STOPCHANNEL, 8, 0x0105 }, + { MLX_CMD_STARTCHANNEL, 13, 0x0005 }, + { MLX_CMD_STARTCHANNEL, 8, 0x0105 }, + { MLX_CMD_DIRECT_CDB, 16, 0x0002 }, + { MLX_CMD_DIRECT_CDB, 17, 0x0008 }, + { MLX_CMD_DIRECT_CDB, 18, 0x000e }, + { MLX_CMD_DIRECT_CDB, 19, 0x000f }, + { MLX_CMD_DIRECT_CDB, 8, 0x0105 }, + + { 0, 20, MLX_STATUS_WEDGED }, + { 0, 21, MLX_STATUS_LOST }, + { 0, 22, MLX_STATUS_BUSY }, + + { 0, 14, 0x0104 }, +}; + +/* + * Return the current time in seconds - we're not particularly interested in + * precision here. + */ +static __inline__ time_t +mlx_curtime(void) +{ + time_t rt; + int s; + + s = splclock(); + rt = mono_time.tv_sec; + splx(s); + + return (rt); +} + +/* + * Initialise the controller and our interface. + */ +void +mlx_init(struct mlx_softc *mlx, const char *intrstr) +{ + struct mlx_ccb *mc; + struct mlx_enquiry_old *meo; + int rv, fwminor, hscode, hserr, hsparam1, hsparam2, hsmsg; + int size, i, rseg; + const char *wantfwstr; + bus_dma_segment_t seg; + + SIMPLEQ_INIT(&mlx->mlx_ccb_queue); + SLIST_INIT(&mlx->mlx_ccb_freelist); + TAILQ_INIT(&mlx->mlx_ccb_worklist); + + if (intrstr != NULL) + printf("%s: interrupting at %s\n", mlx->mlx_dv.dv_xname, + intrstr); + + /* + * Allocate the scatter/gather lists. + */ + size = MLX_SGL_SIZE * MLX_MAX_QUEUECNT; + + if ((rv = bus_dmamem_alloc(mlx->mlx_dmat, size, PAGE_SIZE, 0, &seg, 1, + &rseg, BUS_DMA_NOWAIT)) != 0) { + printf("%s: unable to allocate sglists, rv = %d\n", + mlx->mlx_dv.dv_xname, rv); + return; + } + + if ((rv = bus_dmamem_map(mlx->mlx_dmat, &seg, rseg, size, + (caddr_t *)&mlx->mlx_sgls, + BUS_DMA_NOWAIT | BUS_DMA_COHERENT)) != 0) { + printf("%s: unable to map sglists, rv = %d\n", + mlx->mlx_dv.dv_xname, rv); + return; + } + + if ((rv = bus_dmamap_create(mlx->mlx_dmat, size, size, 1, 0, + BUS_DMA_NOWAIT, &mlx->mlx_dmamap)) != 0) { + printf("%s: unable to create sglist DMA map, rv = %d\n", + mlx->mlx_dv.dv_xname, rv); + return; + } + + if ((rv = bus_dmamap_load(mlx->mlx_dmat, mlx->mlx_dmamap, + mlx->mlx_sgls, size, NULL, BUS_DMA_NOWAIT)) != 0) { + printf("%s: unable to load sglist DMA map, rv = %d\n", + mlx->mlx_dv.dv_xname, rv); + return; + } + + mlx->mlx_sgls_paddr = mlx->mlx_dmamap->dm_segs[0].ds_addr; + memset(mlx->mlx_sgls, 0, size); + + /* + * Allocate and initalize the CCBs. + */ + mc = malloc(sizeof(*mc) * MLX_MAX_QUEUECNT, M_DEVBUF, M_NOWAIT); + mlx->mlx_ccbs = mc; + + for (i = 0; i < MLX_MAX_QUEUECNT; i++, mc++) { + mc->mc_ident = i; + rv = bus_dmamap_create(mlx->mlx_dmat, MLX_MAX_XFER, + MLX_MAX_SEGS, PAGE_SIZE, 0, + BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW, + &mc->mc_xfer_map); + if (rv != 0) + break; + mlx->mlx_nccbs++; + mlx_ccb_free(mlx, mc); + } + if (mlx->mlx_nccbs != MLX_MAX_QUEUECNT) + printf("%s: %d/%d CCBs usable\n", mlx->mlx_dv.dv_xname, + mlx->mlx_nccbs, MLX_MAX_QUEUECNT); + + /* Disable interrupts before we start talking to the controller */ + (*mlx->mlx_intaction)(mlx, 0); + + /* + * Wait for the controller to come ready, handshaking with the + * firmware if required. This is typically only necessary on + * platforms where the controller BIOS does not run. + */ + hsmsg = 0; + + for (;;) { + hscode = (*mlx->mlx_fw_handshake)(mlx, &hserr, &hsparam1, + &hsparam2); + if (hscode == 0) { + if (hsmsg != 0) + printf("%s: initialization complete\n", + mlx->mlx_dv.dv_xname); + break; + } + + /* Report first time around... */ + if (hsmsg == 0) { + printf("%s: initializing (may take some time)...\n", + mlx->mlx_dv.dv_xname); + hsmsg = 1; + } + + /* Did we get a real message? */ + if (hscode == 2) { + hscode = mlx_fw_message(mlx, hserr, hsparam1, hsparam2); + + /* Fatal initialisation error? */ + if (hscode != 0) + return; + } + } + + /* Send an ENQUIRY2 request to the controller... */ + mlx->mlx_enq2 = mlx_enquire(mlx, MLX_CMD_ENQUIRY2, + sizeof(struct mlx_enquiry2), NULL, 0); + if (mlx->mlx_enq2 == NULL) { + printf("%s: ENQUIRY2 failed\n", mlx->mlx_dv.dv_xname); + return; + } + + /* + * Do quirk/feature related things. + */ + switch (mlx->mlx_iftype) { + case 2: + /* + * These controllers may not report the firmware version in + * the ENQUIRY2 response. + */ + meo = mlx_enquire(mlx, MLX_CMD_ENQUIRY_OLD, + sizeof(struct mlx_enquiry_old), NULL, 0); + if (meo == NULL) { + printf("%s: ENQUIRY_OLD failed\n", mlx->mlx_dv.dv_xname); + return; + } + mlx->mlx_enq2->me_firmware_id[0] = meo->me_fwmajor; + mlx->mlx_enq2->me_firmware_id[1] = meo->me_fwminor; + mlx->mlx_enq2->me_firmware_id[2] = '0'; + mlx->mlx_enq2->me_firmware_id[3] = 0; + free(meo, M_DEVBUF); + } + + wantfwstr = NULL; + fwminor = mlx->mlx_enq2->me_firmware_id[1]; + + switch (mlx->mlx_enq2->me_firmware_id[0]) { + case 2: + if ((mlx->mlx_flags & MLXF_EISA) != 0) { + if (fwminor < 14) + wantfwstr = "2.14"; + } else if (fwminor < 42) + wantfwstr = "2.42"; + break; + + case 3: + if (fwminor < 51) + wantfwstr = "3.51"; + break; + + case 4: + if (fwminor < 6) + wantfwstr = "4.06"; + break; + + case 5: + if (fwminor < 7) + wantfwstr = "5.07"; + break; + } + + /* Print a little information about the controller. */ + mlx_describe(mlx); + + if (wantfwstr != NULL) { + printf("%s: WARNING: this f/w revision is not recommended\n", + mlx->mlx_dv.dv_xname); + printf("%s: WARNING: use revision %s or later\n", + mlx->mlx_dv.dv_xname, wantfwstr); + } + + /* We don't (yet) know where the event log is up to. */ + mlx->mlx_currevent = -1; + + /* No user-requested background operation is in progress. */ + mlx->mlx_bg = 0; + mlx->mlx_rebuildstat.rs_code = MLX_REBUILDSTAT_IDLE; + + /* Set maximum number of queued commands for `regular' operations. */ + mlx->mlx_max_queuecnt = + min(le16toh(mlx->mlx_enq2->me_max_commands), MLX_MAX_QUEUECNT) - + MLX_NCCBS_RESERVE; +#ifdef DIAGNOSTIC + if (mlx->mlx_max_queuecnt < MLX_NCCBS_RESERVE + MLX_MAX_DRIVES) + printf("%s: WARNING: few CCBs available\n", + mlx->mlx_dv.dv_xname); + if (le16toh(mlx->mlx_enq2->me_max_sg) < MLX_MAX_SEGS) { + printf("%s: oops, not enough S/G segments\n", + mlx->mlx_dv.dv_xname); + return; + } +#endif + + if (mlx_sdh == NULL) { + /* + * Set our `shutdownhook' before we start any device + * activity. + */ + mlx_sdh = shutdownhook_establish(mlx_shutdown, NULL); + + /* Arrange to create a status monitoring thread. */ + kthread_create(mlx_periodic_create, NULL); + } + + /* Finally, attach child devices and enable interrupts. */ + mlx_configure(mlx, 0); + (*mlx->mlx_intaction)(mlx, 1); + + mlx->mlx_flags |= MLXF_INITOK; +} + +/* + * Tell the world about the controller. + */ +static void +mlx_describe(struct mlx_softc *mlx) +{ + struct mlx_enquiry2 *me; + static char buf[80]; + const char *model; + int i; + + model = NULL; + me = mlx->mlx_enq2; + + for (i = 0; i < sizeof(mlx_cname) / sizeof(mlx_cname[0]); i++) + if (me->me_hardware_id[0] == mlx_cname[i].hwid) { + model = mlx_cname[i].name; + break; + } + + if (model == NULL) { + sprintf(buf, " model 0x%x", me->me_hardware_id[0]); + model = buf; + } + + printf("%s: DAC%s, %d channel%s, firmware %d.%02d-%c-%02d, %dMB RAM\n", + mlx->mlx_dv.dv_xname, model, me->me_actual_channels, + me->me_actual_channels > 1 ? "s" : "", + me->me_firmware_id[0], me->me_firmware_id[1], + me->me_firmware_id[2], me->me_firmware_id[3], + le32toh(me->me_mem_size) >> 20); +} + +/* + * Locate disk resources and attach children to them. + */ +static void +mlx_configure(struct mlx_softc *mlx, int waitok) +{ + struct mlx_enq_sys_drive *mes; + struct mlx_sysdrive *ms; + struct mlx_attach_args mlxa; + int i, nunits; + u_int size; + + mes = mlx_enquire(mlx, MLX_CMD_ENQSYSDRIVE, + sizeof(*mes) * MLX_MAX_DRIVES, NULL, waitok); + if (mes == NULL) { + printf("%s: error fetching drive status\n", + mlx->mlx_dv.dv_xname); + return; + } + + for (i = 0, nunits = 0; i < MLX_MAX_DRIVES; i++) + if (mes[i].sd_size != 0xffffffffU && mes[i].sd_size != 0) + nunits++; + if (nunits == 0) + nunits = 1; + + /* Allow 1 queued command per unit while re-configuring. */ + mlx_adjqparam(mlx, 1, 0); + + ms = &mlx->mlx_sysdrive[0]; + for (i = 0; i < MLX_MAX_DRIVES; i++, ms++) { + size = le32toh(mes[i].sd_size); + + /* + * If an existing device has changed in some way (e.g. no + * longer present) then detach it. + */ + if (ms->ms_dv != NULL && (size != ms->ms_size || + (mes[i].sd_raidlevel & 0xf) != ms->ms_raidlevel)) + config_detach(ms->ms_dv, DETACH_FORCE); + + ms->ms_size = size; + ms->ms_raidlevel = mes[i].sd_raidlevel & 0xf; + ms->ms_state = mes[i].sd_state; + ms->ms_dv = NULL; + + if (size == 0xffffffffU || size == 0) + continue; + + /* + * Attach a new device. + */ + mlxa.mlxa_unit = i; + ms->ms_dv = config_found_sm(&mlx->mlx_dv, &mlxa, mlx_print, + mlx_submatch); + } + + free(mes, M_DEVBUF); + mlx_adjqparam(mlx, mlx->mlx_max_queuecnt / nunits, + mlx->mlx_max_queuecnt % nunits); +} + +/* + * Print autoconfiguration message for a sub-device. + */ +static int +mlx_print(void *aux, const char *pnp) +{ + struct mlx_attach_args *mlxa; + + mlxa = (struct mlx_attach_args *)aux; + + if (pnp != NULL) + printf("block device at %s", pnp); + printf(" unit %d", mlxa->mlxa_unit); + return (UNCONF); +} + +/* + * Match a sub-device. + */ +static int +mlx_submatch(struct device *parent, struct cfdata *cf, void *aux) +{ + struct mlx_attach_args *mlxa; + + mlxa = (struct mlx_attach_args *)aux; + + if (cf->mlxacf_unit != MLXCF_UNIT_DEFAULT && + cf->mlxacf_unit != mlxa->mlxa_unit) + return (0); + + return ((*cf->cf_attach->ca_match)(parent, cf, aux)); +} + +/* + * Shut down all configured `mlx' devices. + */ +static void +mlx_shutdown(void *cookie) +{ + struct mlx_softc *mlx; + int i; + + for (i = 0; i < mlx_cd.cd_ndevs; i++) + if ((mlx = device_lookup(&mlx_cd, i)) != NULL) + mlx_flush(mlx, 0); +} + +/* + * Adjust queue parameters for all child devices. + */ +static void +mlx_adjqparam(struct mlx_softc *mlx, int mpu, int slop) +{ +#if NLD > 0 + extern struct cfdriver ld_cd; + struct ld_softc *ld; + int i; + + for (i = 0; i < ld_cd.cd_ndevs; i++) { + if ((ld = device_lookup(&ld_cd, i)) == NULL) + continue; + if (ld->sc_dv.dv_parent != &mlx->mlx_dv) + continue; + ldadjqparam(ld, mpu + (slop-- > 0)); + } +#endif +} + +/* + * Accept an open operation on the control device. + */ +int +mlxopen(dev_t dev, int flag, int mode, struct proc *p) +{ + struct mlx_softc *mlx; + + if ((mlx = device_lookup(&mlx_cd, minor(dev))) == NULL) + return (ENXIO); + if ((mlx->mlx_flags & MLXF_INITOK) == 0) + return (ENXIO); + if ((mlx->mlx_flags & MLXF_OPEN) != 0) + return (EBUSY); + + mlx->mlx_flags |= MLXF_OPEN; + return (0); +} + +/* + * Accept the last close on the control device. + */ +int +mlxclose(dev_t dev, int flag, int mode, struct proc *p) +{ + struct mlx_softc *mlx; + + mlx = device_lookup(&mlx_cd, minor(dev)); + mlx->mlx_flags &= ~MLXF_OPEN; + return (0); +} + +/* + * Handle control operations. + */ +int +mlxioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p) +{ + struct mlx_softc *mlx; + struct mlx_rebuild_request *rb; + struct mlx_rebuild_status *rs; + struct mlx_pause *mp; + struct mlx_sysdrive *ms; + int i, rv, *arg, result; + + mlx = device_lookup(&mlx_cd, minor(dev)); + + rb = (struct mlx_rebuild_request *)data; + rs = (struct mlx_rebuild_status *)data; + arg = (int *)data; + rv = 0; + + switch (cmd) { + case MLX_RESCAN_DRIVES: + /* + * Scan the controller to see whether new drives have + * appeared, or old ones disappeared. + */ + mlx_configure(mlx, 1); + return (0); + + case MLX_PAUSE_CHANNEL: + /* + * Pause one or more SCSI channels for a period of time, to + * assist in the process of hot-swapping devices. + * + * Note that at least the 3.51 firmware on the DAC960PL + * doesn't seem to do this right. + */ + if ((mlx->mlx_flags & MLXF_PAUSEWORKS) == 0) + return (EOPNOTSUPP); + + mp = (struct mlx_pause *)data; + + if ((mp->mp_which == MLX_PAUSE_CANCEL) && + (mlx->mlx_pause.mp_when != 0)) { + /* Cancel a pending pause operation. */ + mlx->mlx_pause.mp_which = 0; + break; + } + + /* Fix for legal channels. */ + mp->mp_which &= ((1 << mlx->mlx_enq2->me_actual_channels) -1); + + /* Check time values. */ + if (mp->mp_when < 0 || mp->mp_when > 3600 || + mp->mp_howlong < 1 || mp->mp_howlong > (0xf * 30)) { + rv = EINVAL; + break; + } + + /* Check for a pause currently running. */ + if ((mlx->mlx_pause.mp_which != 0) && + (mlx->mlx_pause.mp_when == 0)) { + rv = EBUSY; + break; + } + + /* Looks ok, go with it. */ + mlx->mlx_pause.mp_which = mp->mp_which; + mlx->mlx_pause.mp_when = mlx_curtime() + mp->mp_when; + mlx->mlx_pause.mp_howlong = + mlx->mlx_pause.mp_when + mp->mp_howlong; + + return (0); + + case MLX_COMMAND: + /* + * Accept a command passthrough-style. + */ + return (mlx_user_command(mlx, (struct mlx_usercommand *)data)); + + case MLX_REBUILDASYNC: + /* + * Start a rebuild on a given SCSI disk + */ + if (mlx->mlx_bg != 0) { + rb->rr_status = 0x0106; + rv = EBUSY; + break; + } + + rb->rr_status = mlx_rebuild(mlx, rb->rr_channel, rb->rr_target); + switch (rb->rr_status) { + case 0: + rv = 0; + break; + case 0x10000: + rv = ENOMEM; /* Couldn't set up the command. */ + break; + case 0x0002: + rv = EBUSY; + break; + case 0x0104: + rv = EIO; + break; + case 0x0105: + rv = ERANGE; + break; + case 0x0106: + rv = EBUSY; + break; + default: + rv = EINVAL; + break; + } + + if (rv == 0) + mlx->mlx_bg = MLX_BG_REBUILD; + + return (0); + + case MLX_REBUILDSTAT: + /* + * Get the status of the current rebuild or consistency check. + */ + *rs = mlx->mlx_rebuildstat; + return (0); + + case MLX_GET_SYSDRIVE: + /* + * Return the system drive number matching the `ld' device + * unit in (arg), if it happens to belong to us. + */ + for (i = 0; i < MLX_MAX_DRIVES; i++) { + ms = &mlx->mlx_sysdrive[i]; + if (ms->ms_dv != NULL) + if (ms->ms_dv->dv_xname[2] == '0' + *arg) + return (i); + } + return (ENOENT); + } + + switch (cmd) { + case MLXD_DETACH: + case MLXD_STATUS: + case MLXD_CHECKASYNC: + if ((u_int)*arg >= MLX_MAX_DRIVES) + return (EINVAL); + ms = &mlx->mlx_sysdrive[*arg]; + if (*arg > MLX_MAX_DRIVES || ms->ms_dv == NULL) + return (ENOENT); + break; + + default: + return (ENOTTY); + } + + switch (cmd) { + case MLXD_DETACH: + /* + * Disconnect from the specified drive; it may be about to go + * away. + */ + return (config_detach(ms->ms_dv, 0)); + + case MLXD_STATUS: + /* + * Return the current status of this drive. + */ + *arg = ms->ms_state; + return (0); + + case MLXD_CHECKASYNC: + /* + * Start a background consistency check on this drive. + */ + if (mlx->mlx_bg != 0) { + *arg = 0x0106; + return (EBUSY); + } + + switch (result = mlx_check(mlx, *arg)) { + case 0: + rv = 0; + break; + case 0x10000: + rv = ENOMEM; /* Couldn't set up the command. */ + break; + case 0x0002: + rv = EIO; + break; + case 0x0105: + rv = ERANGE; + break; + case 0x0106: + rv = EBUSY; + break; + default: + rv = EINVAL; + break; + } + + if (rv == 0) + mlx->mlx_bg = MLX_BG_CHECK; + *arg = result; + return (rv); + } + + return (ENOTTY); /* XXX shut up gcc */ +} + +/* + * Fire off commands to periodically check the status of connected drives. + * Check for commands that have timed out. + */ +static void +mlx_periodic_create(void *cookie) +{ + int rv; + + rv = kthread_create1(mlx_periodic_thread, NULL, &mlx_periodic_proc, + "mlxmonitor"); + if (rv == 0) + return; + + printf("mlx_periodic_create: unable to create thread (%d)\n", rv); +} + +static void +mlx_periodic_thread(void *cookie) +{ + struct mlx_softc *mlx; + int i; + + for (;;) { + for (i = 0; i < mlx_cd.cd_ndevs; i++) + if ((mlx = device_lookup(&mlx_cd, i)) != NULL) + mlx_periodic(mlx); + + tsleep(mlx_periodic_thread, PWAIT, "mlxzzz", hz); + } +} + +static void +mlx_periodic(struct mlx_softc *mlx) +{ + struct mlx_ccb *mc, *nmc; + int etype, s; + time_t ct; + + ct = mlx_curtime(); + + if ((mlx->mlx_pause.mp_which != 0) && + (mlx->mlx_pause.mp_when > 0) && + (ct >= mlx->mlx_pause.mp_when)) { + /* + * Start bus pause. + */ + mlx_pause_action(mlx); + mlx->mlx_pause.mp_when = 0; +#ifdef notdef + sysbeep(500, hz); + +#endif + } else if ((mlx->mlx_pause.mp_which != 0) && + (mlx->mlx_pause.mp_when == 0)) { + /* + * Stop pause if required. + */ + if (ct >= mlx->mlx_pause.mp_howlong) { + mlx_pause_action(mlx); + mlx->mlx_pause.mp_which = 0; + } +#ifdef notdef + sysbeep(500, hz); + } else + sysbeep((ct % 5) * 100 + 500, hz/8); +#endif + } else if (ct > (mlx->mlx_lastpoll + 10)) { + /* + * Run normal periodic activities... + */ + mlx->mlx_lastpoll = ct; + + /* + * Check controller status. + */ + if ((mlx->mlx_flags & MLXF_PERIODIC_CTLR) == 0) { + mlx->mlx_flags |= MLXF_PERIODIC_CTLR; + + if (mlx->mlx_iftype == 2) + etype = MLX_CMD_ENQUIRY_OLD; + else + etype = MLX_CMD_ENQUIRY; + + mlx_enquire(mlx, etype, max(sizeof(struct mlx_enquiry), + sizeof(struct mlx_enquiry_old)), + mlx_periodic_enquiry, 1); + } + + /* + * Check system drive status. + */ + if ((mlx->mlx_flags & MLXF_PERIODIC_DRIVE) == 0) { + mlx->mlx_flags |= MLXF_PERIODIC_DRIVE; + mlx_enquire(mlx, MLX_CMD_ENQSYSDRIVE, + sizeof(struct mlx_enq_sys_drive) * MLX_MAX_DRIVES, + mlx_periodic_enquiry, 1); + } + } + + /* + * Get drive rebuild/check status. + */ + if ((mlx->mlx_flags & MLXF_PERIODIC_REBUILD) == 0) { + mlx->mlx_flags |= MLXF_PERIODIC_REBUILD; + mlx_enquire(mlx, MLX_CMD_REBUILDSTAT, + sizeof(struct mlx_rebuild_stat), mlx_periodic_rebuild, 1); + } + + /* + * Time-out busy CCBs. + */ + s = splbio(); + for (mc = TAILQ_FIRST(&mlx->mlx_ccb_worklist); mc != NULL; mc = nmc) { + nmc = TAILQ_NEXT(mc, mc_chain.tailq); + if (mc->mc_expiry > ct) { + /* + * The remaining CCBs will expire after this one, so + * there's no point in going further. + */ + break; + } + TAILQ_REMOVE(&mlx->mlx_ccb_worklist, mc, mc_chain.tailq); + mc->mc_status = MLX_STATUS_LOST; + if (mc->mc_mx.mx_handler != NULL) + (*mc->mc_mx.mx_handler)(mc); + else if ((mc->mc_flags & MC_WAITING) != 0) + wakeup(mc); + } + splx(s); +} + +/* + * Handle the result of an ENQUIRY command instigated by periodic status + * polling. + */ +static void +mlx_periodic_enquiry(struct mlx_ccb *mc) +{ + struct mlx_softc *mlx; + struct mlx_enquiry *me; + struct mlx_enquiry_old *meo; + struct mlx_enq_sys_drive *mes; + struct mlx_sysdrive *dr; + const char *statestr; + int i, j; + u_int lsn; + + mlx = (struct mlx_softc *)mc->mc_mx.mx_dv; + + /* + * Command completed OK? + */ + if (mc->mc_status != 0) { + printf("%s: periodic enquiry failed - %s\n", + mlx->mlx_dv.dv_xname, mlx_ccb_diagnose(mc)); + goto out; + } + + /* + * Respond to command. + */ + switch (mc->mc_mbox[0]) { + case MLX_CMD_ENQUIRY_OLD: + /* + * This is currently a bit fruitless, as we don't know how + * to extract the eventlog pointer yet. + */ + me = (struct mlx_enquiry *)mc->mc_mx.mx_context; + meo = (struct mlx_enquiry_old *)mc->mc_mx.mx_context; + + /* Convert data in-place to new format */ + i = sizeof(me->me_dead) / sizeof(me->me_dead[0]); + while (--i >= 0) { + me->me_dead[i].dd_chan = meo->me_dead[i].dd_chan; + me->me_dead[i].dd_targ = meo->me_dead[i].dd_targ; + } + + me->me_misc_flags = 0; + me->me_rebuild_count = meo->me_rebuild_count; + me->me_dead_count = meo->me_dead_count; + me->me_critical_sd_count = meo->me_critical_sd_count; + me->me_event_log_seq_num = 0; + me->me_offline_sd_count = meo->me_offline_sd_count; + me->me_max_commands = meo->me_max_commands; + me->me_rebuild_flag = meo->me_rebuild_flag; + me->me_fwmajor = meo->me_fwmajor; + me->me_fwminor = meo->me_fwminor; + me->me_status_flags = meo->me_status_flags; + me->me_flash_age = meo->me_flash_age; + + i = sizeof(me->me_drvsize) / sizeof(me->me_drvsize[0]); + j = sizeof(meo->me_drvsize) / sizeof(meo->me_drvsize[0]); + + while (--i >= 0) { + if (i >= j) + me->me_drvsize[i] = 0; + else + me->me_drvsize[i] = meo->me_drvsize[i]; + } + + me->me_num_sys_drvs = meo->me_num_sys_drvs; + + /* FALLTHROUGH */ + + case MLX_CMD_ENQUIRY: + /* + * Generic controller status update. We could do more with + * this than just checking the event log. + */ + me = (struct mlx_enquiry *)mc->mc_mx.mx_context; + lsn = le16toh(me->me_event_log_seq_num); + + if (mlx->mlx_currevent == -1) { + /* Initialise our view of the event log. */ + mlx->mlx_currevent = lsn; + mlx->mlx_lastevent = lsn; + } else if (lsn != mlx->mlx_lastevent && + (mlx->mlx_flags & MLXF_EVENTLOG_BUSY) == 0) { + /* Record where current events are up to */ + mlx->mlx_currevent = lsn; + + /* Mark the event log as busy. */ + mlx->mlx_flags |= MLXF_EVENTLOG_BUSY; + + /* Drain new eventlog entries. */ + mlx_periodic_eventlog_poll(mlx); + } + break; + + case MLX_CMD_ENQSYSDRIVE: + mes = (struct mlx_enq_sys_drive *)mc->mc_mx.mx_context; + + dr = &mlx->mlx_sysdrive[0]; + + for (i = 0; i < MLX_MAX_DRIVES; i++) { + if (mes[i].sd_size == 0xffffffffU || + mes[i].sd_size == 0) + break; + + /* Has state been changed by controller? */ + if (dr->ms_state != mes[i].sd_state) { + switch (mes[i].sd_state) { + case MLX_SYSD_OFFLINE: + statestr = "offline"; + break; + + case MLX_SYSD_ONLINE: + statestr = "online"; + break; + + case MLX_SYSD_CRITICAL: + statestr = "critical"; + break; + } + + printf("%s: unit %d %s\n", mlx->mlx_dv.dv_xname, + i, statestr); + + /* Save new state. */ + dr->ms_state = mes[i].sd_state; + } + } + break; + +#ifdef DIAGNOSTIC + default: + printf("%s: mlx_periodic_enquiry: eh?\n", + mlx->mlx_dv.dv_xname); + break; +#endif + } + +out: + if (mc->mc_mbox[0] == MLX_CMD_ENQSYSDRIVE) + mlx->mlx_flags &= ~MLXF_PERIODIC_DRIVE; + else + mlx->mlx_flags &= ~MLXF_PERIODIC_CTLR; + + free(mc->mc_mx.mx_context, M_DEVBUF); + mlx_ccb_free(mlx, mc); +} + +/* + * Instigate a poll for one event log message on (mlx). We only poll for + * one message at a time, to keep our command usage down. + */ +static void +mlx_periodic_eventlog_poll(struct mlx_softc *mlx) +{ + struct mlx_ccb *mc; + void *result; + int rv; + + result = NULL; + + if ((rv = mlx_ccb_alloc(mlx, &mc, 1)) != 0) + goto out; + + if ((result = malloc(1024, M_DEVBUF, M_WAITOK)) == NULL) { + rv = ENOMEM; + goto out; + } + if ((rv = mlx_ccb_map(mlx, mc, result, 1024, MC_XFER_IN)) != 0) + goto out; + + /* Build the command to get one log entry. */ + mlx_make_type3(mc, MLX_CMD_LOGOP, MLX_LOGOP_GET, 1, + mlx->mlx_lastevent, 0, 0, mc->mc_xfer_phys, 0); + + mc->mc_mx.mx_handler = mlx_periodic_eventlog_respond; + mc->mc_mx.mx_dv = &mlx->mlx_dv; + mc->mc_mx.mx_context = result; + + /* Start the command. */ + mlx_ccb_enqueue(mlx, mc); + +out: + if (rv != 0) { + if (mc != NULL) + mlx_ccb_free(mlx, mc); + if (result != NULL) + free(result, M_DEVBUF); + } +} + +/* + * Handle the result of polling for a log message, generate diagnostic + * output. If this wasn't the last message waiting for us, we'll go collect + * another. + */ +static void +mlx_periodic_eventlog_respond(struct mlx_ccb *mc) +{ + struct mlx_softc *mlx; + struct mlx_eventlog_entry *el; + const char *reason; + u_int8_t sensekey, chan, targ; + + mlx = (struct mlx_softc *)mc->mc_mx.mx_dv; + el = mc->mc_mx.mx_context; + mlx_ccb_unmap(mlx, mc); + + mlx->mlx_lastevent++; + + if (mc->mc_status == 0) { + switch (el->el_type) { + case MLX_LOGMSG_SENSE: /* sense data */ + sensekey = el->el_sense & 0x0f; + chan = (el->el_target >> 4) & 0x0f; + targ = el->el_target & 0x0f; + + /* + * This is the only sort of message we understand at + * the moment. The tests here are probably + * incomplete. + */ + + /* + * Mylex vendor-specific message indicating a drive + * was killed? + */ + if (sensekey == 9 && el->el_asc == 0x80) { + if (el->el_asq < sizeof(mlx_sense_msgs) / + sizeof(mlx_sense_msgs[0])) + reason = mlx_sense_msgs[el->el_asq]; + else + reason = "for unknown reason"; + + printf("%s: physical drive %d:%d killed %s\n", + mlx->mlx_dv.dv_xname, chan, targ, reason); + } + + /* + * SCSI drive was reset? + */ + if (sensekey == 6 && el->el_asc == 0x29) + printf("%s: physical drive %d:%d reset\n", + mlx->mlx_dv.dv_xname, chan, targ); + + /* + * SCSI drive error? + */ + if (!(sensekey == 0 || + (sensekey == 2 && + el->el_asc == 0x04 && + (el->el_asq == 0x01 || el->el_asq == 0x02)))) { + printf("%s: physical drive %d:%d error log: " + "sense = %d asc = %x asq = %x\n", + mlx->mlx_dv.dv_xname, chan, targ, sensekey, + el->el_asc, el->el_asq); + printf("%s: info = %d:%d:%d:%d " + " csi = %d:%d:%d:%d\n", mlx->mlx_dv.dv_xname, + el->el_information[0], el->el_information[1], + el->el_information[2], el->el_information[3], + el->el_csi[0], el->el_csi[1], + el->el_csi[2], el->el_csi[3]); + } + + break; + + default: + printf("%s: unknown log message type 0x%x\n", + mlx->mlx_dv.dv_xname, el->el_type); + break; + } + } else { + printf("%s: error reading message log - %s\n", + mlx->mlx_dv.dv_xname, mlx_ccb_diagnose(mc)); + + /* + * Give up on all the outstanding messages, as we may have + * come unsynched. + */ + mlx->mlx_lastevent = mlx->mlx_currevent; + } + + free(mc->mc_mx.mx_context, M_DEVBUF); + mlx_ccb_free(mlx, mc); + + /* + * Is there another message to obtain? + */ + if (mlx->mlx_lastevent != mlx->mlx_currevent) + mlx_periodic_eventlog_poll(mlx); + else + mlx->mlx_flags &= ~MLXF_EVENTLOG_BUSY; +} + +/* + * Handle check/rebuild operations in progress. + */ +static void +mlx_periodic_rebuild(struct mlx_ccb *mc) +{ + struct mlx_softc *mlx; + const char *opstr; + struct mlx_rebuild_status *mr; + + mlx = (struct mlx_softc *)mc->mc_mx.mx_dv; + mr = mc->mc_mx.mx_context; + mlx_ccb_unmap(mlx, mc); + + switch (mc->mc_status) { + case 0: + /* + * Operation running, update stats. + */ + mlx->mlx_rebuildstat = *mr; + + /* Spontaneous rebuild/check? */ + if (mlx->mlx_bg == 0) { + mlx->mlx_bg = MLX_BG_SPONTANEOUS; + printf("%s: background check/rebuild started\n", + mlx->mlx_dv.dv_xname); + } + break; + + case 0x0105: + /* + * Nothing running, finalise stats and report. + */ + switch (mlx->mlx_bg) { + case MLX_BG_CHECK: + /* XXX Print drive? */ + opstr = "consistency check"; + break; + + case MLX_BG_REBUILD: + /* XXX Print channel:target? */ + opstr = "drive rebuild"; + break; + + case MLX_BG_SPONTANEOUS: + default: + /* + * If we have previously been non-idle, report the + * transition + */ + if (mlx->mlx_rebuildstat.rs_code != + MLX_REBUILDSTAT_IDLE) + opstr = "background check/rebuild"; + else + opstr = NULL; + } + + if (opstr != NULL) + printf("%s: %s completed\n", mlx->mlx_dv.dv_xname, + opstr); + + mlx->mlx_bg = 0; + mlx->mlx_rebuildstat.rs_code = MLX_REBUILDSTAT_IDLE; + break; + } + + free(mc->mc_mx.mx_context, M_DEVBUF); + mlx_ccb_free(mlx, mc); + mlx->mlx_flags &= ~MLXF_PERIODIC_REBUILD; +} + +/* + * It's time to perform a channel pause action for (mlx), either start or + * stop the pause. + */ +static void +mlx_pause_action(struct mlx_softc *mlx) +{ + struct mlx_ccb *mc; + int failsafe, i, cmd; + time_t ct; + + ct = mlx_curtime(); + + /* What are we doing here? */ + if (mlx->mlx_pause.mp_when == 0) { + cmd = MLX_CMD_STARTCHANNEL; + failsafe = 0; + } else { + cmd = MLX_CMD_STOPCHANNEL; + + /* + * Channels will always start again after the failsafe + * period, which is specified in multiples of 30 seconds. + * This constrains us to a maximum pause of 450 seconds. + */ + failsafe = ((mlx->mlx_pause.mp_howlong - ct) + 5) / 30; + + if (failsafe > 0xf) { + failsafe = 0xf; + mlx->mlx_pause.mp_howlong = ct + (0xf * 30) - 5; + } + } + + /* Build commands for every channel requested. */ + for (i = 0; i < mlx->mlx_enq2->me_actual_channels; i++) { + if ((1 << i) & mlx->mlx_pause.mp_which) { + if (mlx_ccb_alloc(mlx, &mc, 1) != 0) { + printf("%s: %s failed for channel %d\n", + mlx->mlx_dv.dv_xname, + cmd == MLX_CMD_STOPCHANNEL ? + "pause" : "resume", i); + continue; + } + + /* Build the command. */ + mlx_make_type2(mc, cmd, (failsafe << 4) | i, 0, 0, + 0, 0, 0, 0, 0); + mc->mc_mx.mx_handler = mlx_pause_done; + mc->mc_mx.mx_dv = &mlx->mlx_dv; + + mlx_ccb_enqueue(mlx, mc); + } + } +} + +static void +mlx_pause_done(struct mlx_ccb *mc) +{ + struct mlx_softc *mlx; + int command, channel; + + mlx = (struct mlx_softc *)mc->mc_mx.mx_dv; + command = mc->mc_mbox[0]; + channel = mc->mc_mbox[2] & 0xf; + + if (mc->mc_status != 0) + printf("%s: %s command failed - %s\n", mlx->mlx_dv.dv_xname, + command == MLX_CMD_STOPCHANNEL ? "pause" : "resume", + mlx_ccb_diagnose(mc)); + else if (command == MLX_CMD_STOPCHANNEL) + printf("%s: channel %d pausing for %ld seconds\n", + mlx->mlx_dv.dv_xname, channel, + (long)(mlx->mlx_pause.mp_howlong - mlx_curtime())); + else + printf("%s: channel %d resuming\n", mlx->mlx_dv.dv_xname, + channel); + + mlx_ccb_free(mlx, mc); +} + +/* + * Perform an Enquiry command using a type-3 command buffer and a return a + * single linear result buffer. If the completion function is specified, it + * will be called with the completed command (and the result response will + * not be valid until that point). Otherwise, the command will either be + * busy-waited for (interrupts must be blocked), or slept for. + */ +static void * +mlx_enquire(struct mlx_softc *mlx, int command, size_t bufsize, + void (*handler)(struct mlx_ccb *mc), int waitok) +{ + struct mlx_ccb *mc; + void *result; + int rv, mapped; + + result = NULL; + mapped = 0; + + if ((rv = mlx_ccb_alloc(mlx, &mc, 1)) != 0) + goto out; + + result = malloc(bufsize, M_DEVBUF, waitok ? M_WAITOK : M_NOWAIT); + if (result == NULL) { + printf("mlx_enquire: malloc() failed\n"); + goto out; + } + if ((rv = mlx_ccb_map(mlx, mc, result, bufsize, MC_XFER_IN)) != 0) + goto out; + mapped = 1; + + /* Build an enquiry command. */ + mlx_make_type2(mc, command, 0, 0, 0, 0, 0, 0, mc->mc_xfer_phys, 0); + + /* Do we want a completion callback? */ + if (handler != NULL) { + mc->mc_mx.mx_context = result; + mc->mc_mx.mx_dv = &mlx->mlx_dv; + mc->mc_mx.mx_handler = handler; + mlx_ccb_enqueue(mlx, mc); + } else { + /* Run the command in either polled or wait mode. */ + if (waitok) { + if ((rv = mlx_ccb_wait(mlx, mc)) != 0) + goto out; + } else { + if ((rv = mlx_ccb_poll(mlx, mc, 5000)) != 0) + goto out; + } + + /* Command completed OK? */ + if (mc->mc_status != 0) + goto out; + } + + rv = 0; +out: + /* We got a command, but nobody else will free it. */ + if (handler == NULL && mc != NULL) { + if (mapped) + mlx_ccb_unmap(mlx, mc); + mlx_ccb_free(mlx, mc); + } + + /* We got an error, and we allocated a result. */ + if (rv != 0 && result != NULL) { + if (handler != NULL && mc != NULL) { + if (mapped) + mlx_ccb_unmap(mlx, mc); + mlx_ccb_free(mlx, mc); + } + free(result, M_DEVBUF); + result = NULL; + } + + return (result); +} + +/* + * Perform a Flush command on the nominated controller. + * + * May be called with interrupts enabled or disabled; will not return until + * the flush operation completes or fails. + */ +int +mlx_flush(struct mlx_softc *mlx, int async) +{ + struct mlx_ccb *mc; + int rv; + + if ((rv = mlx_ccb_alloc(mlx, &mc, 1)) != 0) + goto out; + + /* Build a flush command and fire it off. */ + mlx_make_type2(mc, MLX_CMD_FLUSH, 0, 0, 0, 0, 0, 0, 0, 0); + + if (async) + rv = mlx_ccb_wait(mlx, mc); + else + rv = mlx_ccb_poll(mlx, mc, MLX_TIMEOUT * 1000); + if (rv != 0) + goto out; + + /* Command completed OK? */ + if (mc->mc_status != 0) { + printf("%s: FLUSH failed - %s\n", mlx->mlx_dv.dv_xname, + mlx_ccb_diagnose(mc)); + rv = EIO; + } +out: + if (mc != NULL) + mlx_ccb_free(mlx, mc); + + return (rv); +} + +/* + * Start a background consistency check on (drive). + * + * May be called with interrupts enabled or disabled; will return as soon as + * the operation has started or been refused. + */ +static int +mlx_check(struct mlx_softc *mlx, int drive) +{ + struct mlx_ccb *mc; + int rv; + + /* Get ourselves a command buffer. */ + rv = 0x10000; + + if (mlx_ccb_alloc(mlx, &mc, 1) != 0) + goto out; + + /* Build a checkasync command, set the "fix it" flag. */ + mlx_make_type2(mc, MLX_CMD_CHECKASYNC, 0, 0, 0, 0, 0, drive | 0x80, + 0, 0); + + /* Start the command and wait for it to be returned. */ + if (mlx_ccb_wait(mlx, mc) != 0) + goto out; + + /* Command completed OK? */ + if (mc->mc_status != 0) + printf("%s: CHECK ASYNC failed - %s\n", mlx->mlx_dv.dv_xname, + mlx_ccb_diagnose(mc)); + else + printf("%s: consistency check started", + mlx->mlx_sysdrive[drive].ms_dv->dv_xname); + + rv = mc->mc_status; +out: + if (mc != NULL) + mlx_ccb_free(mlx, mc); + + return (rv); +} + +/* + * Start a background rebuild of the physical drive at (channel),(target). + * + * May be called with interrupts enabled or disabled; will return as soon as + * the operation has started or been refused. + */ +static int +mlx_rebuild(struct mlx_softc *mlx, int channel, int target) +{ + struct mlx_ccb *mc; + int error; + + error = 0x10000; + if (mlx_ccb_alloc(mlx, &mc, 1) != 0) + goto out; + + /* Build a rebuildasync command, set the "fix it" flag. */ + mlx_make_type2(mc, MLX_CMD_REBUILDASYNC, channel, target, 0, 0, 0, 0, + 0, 0); + + /* Start the command and wait for it to be returned. */ + if (mlx_ccb_wait(mlx, mc) != 0) + goto out; + + /* Command completed OK? */ + printf("%s: ", mlx->mlx_dv.dv_xname); + if (mc->mc_status != 0) + printf("REBUILD ASYNC failed - %s\n", mlx_ccb_diagnose(mc)); + else + printf("rebuild started for %d:%d\n", channel, target); + + error = mc->mc_status; + +out: + if (mc != NULL) + mlx_ccb_free(mlx, mc); + + return (error); +} + +/* + * Take a command from user-space and try to run it. + * + * XXX Note that this can't perform very much in the way of error checking, + * XXX and as such, applications _must_ be considered trustworthy. + * + * XXX Commands using S/G for data are not supported. + */ +static int +mlx_user_command(struct mlx_softc *mlx, struct mlx_usercommand *mu) +{ + struct mlx_ccb *mc; + struct mlx_dcdb *dcdb; + void *kbuf; + int rv, mapped; + + if ((mu->mu_bufdir & ~MU_XFER_MASK) != 0) + return (EINVAL); + + kbuf = NULL; + dcdb = NULL; + mapped = 0; + + /* Get ourselves a command and copy in from user space. */ + if ((rv = mlx_ccb_alloc(mlx, &mc, 1)) != 0) { + DPRINTF(("mlx_user_command: mlx_ccb_alloc = %d\n", rv)); + goto out; + } + + memcpy(mc->mc_mbox, mu->mu_command, sizeof(mc->mc_mbox)); + + /* + * If we need a buffer for data transfer, allocate one and copy in + * its initial contents. + */ + if (mu->mu_datasize > 0) { + kbuf = malloc(mu->mu_datasize, M_DEVBUF, M_WAITOK); + if (kbuf == NULL) { + DPRINTF(("mlx_user_command: malloc = NULL\n")); + rv = ENOMEM; + goto out; + } + + if ((mu->mu_bufdir & MU_XFER_OUT) != 0) { + rv = copyin(mu->mu_buf, kbuf, mu->mu_datasize); + if (rv != 0) { + DPRINTF(("mlx_user_command: copyin = %d\n", + rv)); + goto out; + } + } + + /* Map the buffer so the controller can see it. */ + rv = mlx_ccb_map(mlx, mc, kbuf, mu->mu_datasize, mu->mu_bufdir); + if (rv != 0) { + DPRINTF(("mlx_user_command: mlx_ccb_map = %d\n", rv)); + goto out; + } + if (mc->mc_nsgent > 1) { + DPRINTF(("mlx_user_command: too many s/g entries\n")); + rv = EFBIG; + goto out; + } + mapped = 1; + } + + /* + * If this is a passthrough SCSI command, the DCDB is packed at the + * beginning of the data area. Fix up the DCDB to point to the correct physical + * address and override any bufptr supplied by the caller since we know + * what it's meant to be. + */ + if (mc->mc_mbox[0] == MLX_CMD_DIRECT_CDB) { + dcdb = (struct mlx_dcdb *)kbuf; + dcdb->dcdb_physaddr = mc->mc_xfer_phys + sizeof(*dcdb); + mu->mu_bufptr = 8; + } + + /* + * If there's a data buffer, fix up the command's buffer pointer. + */ + if (mu->mu_datasize > 0) { + /* Range check the pointer to physical buffer address. */ + if (mu->mu_bufptr < 0 || + mu->mu_bufptr > sizeof(mu->mu_command) - 4) { + DPRINTF(("mlx_user_command: bufptr botch\n")); + rv = EINVAL; + goto out; + } + + mc->mc_mbox[mu->mu_bufptr] = mc->mc_xfer_phys; + mc->mc_mbox[mu->mu_bufptr+1] = mc->mc_xfer_phys >> 8; + mc->mc_mbox[mu->mu_bufptr+2] = mc->mc_xfer_phys >> 16; + mc->mc_mbox[mu->mu_bufptr+3] = mc->mc_xfer_phys >> 24; + } + + /* Submit the command and wait. */ + if ((rv = mlx_ccb_wait(mlx, mc)) != 0) { +#ifdef DEBUG + printf("mlx_user_command: mlx_ccb_wait = %d\n", rv); +#endif + } + +out: + if (mc != NULL) { + if (mapped) + mlx_ccb_unmap(mlx, mc); + mlx_ccb_free(mlx, mc); + } + + /* Copy out status and data */ + mu->mu_status = mc->mc_status; + + if (kbuf != NULL) { + if (mu->mu_datasize > 0 && (mu->mu_bufdir & MU_XFER_IN) != 0) { + rv = copyout(kbuf, mu->mu_buf, mu->mu_datasize); +#ifdef DIAGNOSTIC + if (rv != 0) + printf("mlx_user_command: copyout = %d\n", rv); +#endif + } + } + if (kbuf != NULL) + free(kbuf, M_DEVBUF); + + return (rv); +} + +/* + * Allocate and initialise a CCB. + */ +int +mlx_ccb_alloc(struct mlx_softc *mlx, struct mlx_ccb **mcp, int special) +{ + struct mlx_ccb *mc; + int s; + + s = splbio(); + if ((!special && mlx->mlx_nccbs_free < MLX_NCCBS_RESERVE) || + SLIST_FIRST(&mlx->mlx_ccb_freelist) == NULL) { + splx(s); + *mcp = NULL; + return (EAGAIN); + } + mc = SLIST_FIRST(&mlx->mlx_ccb_freelist); + SLIST_REMOVE_HEAD(&mlx->mlx_ccb_freelist, mc_chain.slist); + mlx->mlx_nccbs_free--; + splx(s); + + *mcp = mc; + return (0); +} + +/* + * Free a CCB. + */ +void +mlx_ccb_free(struct mlx_softc *mlx, struct mlx_ccb *mc) +{ + int s; + + s = splbio(); + mc->mc_flags = 0; + SLIST_INSERT_HEAD(&mlx->mlx_ccb_freelist, mc, mc_chain.slist); + mlx->mlx_nccbs_free++; + splx(s); +} + +/* + * If a CCB is specified, enqueue it. Pull CCBs off the software queue in + * the order that they were enqueued and try to submit their mailboxes to + * the controller for execution. + */ +void +mlx_ccb_enqueue(struct mlx_softc *mlx, struct mlx_ccb *mc) +{ + int s; + + s = splbio(); + + if (mc != NULL) + SIMPLEQ_INSERT_TAIL(&mlx->mlx_ccb_queue, mc, mc_chain.simpleq); + + while ((mc = SIMPLEQ_FIRST(&mlx->mlx_ccb_queue)) != NULL) { + if (mlx_ccb_submit(mlx, mc) != 0) + break; + SIMPLEQ_REMOVE_HEAD(&mlx->mlx_ccb_queue, mc, mc_chain.simpleq); + } + + splx(s); +} + +/* + * Map the specified CCB's data buffer onto the bus, and fill the + * scatter-gather list. + */ +int +mlx_ccb_map(struct mlx_softc *mlx, struct mlx_ccb *mc, void *data, int size, + int dir) +{ + struct mlx_sgentry *sge; + int nsegs, i, rv, sgloff; + bus_dmamap_t xfer; + + xfer = mc->mc_xfer_map; + + rv = bus_dmamap_load(mlx->mlx_dmat, xfer, data, size, NULL, + BUS_DMA_NOWAIT); + if (rv != 0) + return (rv); + + nsegs = xfer->dm_nsegs; + mc->mc_xfer_size = size; + mc->mc_flags |= dir; + mc->mc_nsgent = nsegs; + mc->mc_xfer_phys = xfer->dm_segs[0].ds_addr; + + sgloff = MLX_SGL_SIZE * mc->mc_ident; + sge = (struct mlx_sgentry *)((caddr_t)mlx->mlx_sgls + sgloff); + + for (i = 0; i < nsegs; i++, sge++) { + sge->sge_addr = htole32(xfer->dm_segs[i].ds_addr); + sge->sge_count = htole32(xfer->dm_segs[i].ds_len); + } + + if ((dir & MC_XFER_OUT) != 0) + i = BUS_DMASYNC_PREWRITE; + else + i = 0; + if ((dir & MC_XFER_IN) != 0) + i |= BUS_DMASYNC_PREREAD; + + bus_dmamap_sync(mlx->mlx_dmat, xfer, 0, mc->mc_xfer_size, i); + bus_dmamap_sync(mlx->mlx_dmat, mlx->mlx_dmamap, sgloff, + MLX_SGL_SIZE, BUS_DMASYNC_PREWRITE); + + return (0); +} + +/* + * Unmap the specified CCB's data buffer. + */ +void +mlx_ccb_unmap(struct mlx_softc *mlx, struct mlx_ccb *mc) +{ + int i; + + bus_dmamap_sync(mlx->mlx_dmat, mlx->mlx_dmamap, + MLX_SGL_SIZE * mc->mc_ident, MLX_SGL_SIZE, + BUS_DMASYNC_POSTWRITE); + + if ((mc->mc_flags & MC_XFER_OUT) != 0) + i = BUS_DMASYNC_PREWRITE; + else + i = 0; + if ((mc->mc_flags & MC_XFER_IN) != 0) + i |= BUS_DMASYNC_PREREAD; + + bus_dmamap_sync(mlx->mlx_dmat, mc->mc_xfer_map, 0, mc->mc_xfer_size, i); + bus_dmamap_unload(mlx->mlx_dmat, mc->mc_xfer_map); +} + +/* + * Submit the CCB, and busy-wait for it to complete. Return non-zero on + * timeout or submission error. Must be called with interrupts blocked. + */ +int +mlx_ccb_poll(struct mlx_softc *mlx, struct mlx_ccb *mc, int timo) +{ + int rv; + + mc->mc_mx.mx_handler = NULL; + + if ((rv = mlx_ccb_submit(mlx, mc)) != 0) + return (rv); + + for (timo *= 10; timo != 0; timo--) { + mlx_intr(mlx); + if (mc->mc_status != MLX_STATUS_BUSY) + break; + DELAY(100); + } + + if (timo != 0) { + if (mc->mc_status != 0) { + printf("%s: command failed - %s\n", + mlx->mlx_dv.dv_xname, mlx_ccb_diagnose(mc)); + rv = EIO; + } else + rv = 0; + } else { + printf("%s: command timed out\n", mlx->mlx_dv.dv_xname); + rv = EIO; + } + + return (rv); +} + +/* + * Enqueue the CCB, and sleep until it completes. Return non-zero on + * timeout or error. + */ +int +mlx_ccb_wait(struct mlx_softc *mlx, struct mlx_ccb *mc) +{ + int s; + + mc->mc_flags |= MC_WAITING; + mc->mc_mx.mx_handler = NULL; + + s = splbio(); + mlx_ccb_enqueue(mlx, mc); + tsleep(mc, PRIBIO, "mlxwccb", 0); + splx(s); + + if (mc->mc_status != 0) { + printf("%s: command failed - %s\n", mlx->mlx_dv.dv_xname, + mlx_ccb_diagnose(mc)); + return (EIO); + } + + return (0); +} + +/* + * Try to submit a CCB's mailbox to the controller for execution. Return + * non-zero on timeout or error. Must be called with interrupts blocked. + */ +static int +mlx_ccb_submit(struct mlx_softc *mlx, struct mlx_ccb *mc) +{ + int i, s, r; + + /* Save the ident so we can handle this command when complete. */ + mc->mc_mbox[1] = (u_int8_t)mc->mc_ident; + + /* Mark the command as currently being processed. */ + mc->mc_status = MLX_STATUS_BUSY; + mc->mc_expiry = mlx_curtime() + MLX_TIMEOUT; + + /* Spin waiting for the mailbox. */ + for (i = 100; i != 0; i--) { + s = splbio(); + r = (*mlx->mlx_submit)(mlx, mc); + splx(s); + if (r != 0) + break; + DELAY(100); + } + + if (i != 0) { + TAILQ_INSERT_TAIL(&mlx->mlx_ccb_worklist, mc, mc_chain.tailq); + return (0); + } + + DPRINTF(("mlx_ccb_submit: rejected; queueing\n")); + mc->mc_status = MLX_STATUS_WEDGED; + return (EIO); +} + +/* + * Return a string that describes why a command has failed. + */ +const char * +mlx_ccb_diagnose(struct mlx_ccb *mc) +{ + static char buf[80]; + int i; + + for (i = 0; i < sizeof(mlx_msgs) / sizeof(mlx_msgs[0]); i++) + if ((mc->mc_mbox[0] == mlx_msgs[i].command || + mlx_msgs[i].command == 0) && + mc->mc_status == mlx_msgs[i].status) { + sprintf(buf, "%s (0x%x)", + mlx_status_msgs[mlx_msgs[i].msg], mc->mc_status); + return (buf); + } + + sprintf(buf, "unknown response 0x%x for command 0x%x", + (int)mc->mc_status, (int)mc->mc_mbox[0]); + + return (buf); +} + +/* + * Poll the controller for completed commands. Returns non-zero if one or + * more commands were completed. Must be called with interrupts blocked. + */ +int +mlx_intr(void *cookie) +{ + struct mlx_softc *mlx; + struct mlx_ccb *mc; + int result; + u_int ident, status; + + mlx = cookie; + result = 0; + + while ((*mlx->mlx_findcomplete)(mlx, &ident, &status) != 0) { + result = 1; + + if (ident >= MLX_MAX_QUEUECNT) { + printf("%s: bad completion returned\n", + mlx->mlx_dv.dv_xname); + continue; + } + + mc = mlx->mlx_ccbs + ident; + + if (mc->mc_status != MLX_STATUS_BUSY) { + printf("%s: bad completion returned\n", + mlx->mlx_dv.dv_xname); + continue; + } + + TAILQ_REMOVE(&mlx->mlx_ccb_worklist, mc, mc_chain.tailq); + + /* Record status and notify the initiator, if requested. */ + mc->mc_status = status; + if (mc->mc_mx.mx_handler != NULL) + (*mc->mc_mx.mx_handler)(mc); + else if ((mc->mc_flags & MC_WAITING) != 0) + wakeup(mc); + } + + /* If we've completed any commands, try posting some more. */ + if (result) + mlx_ccb_enqueue(mlx, NULL); + + return (result); +} + +/* + * Emit a string describing the firmware handshake status code, and return a + * flag indicating whether the code represents a fatal error. + * + * Error code interpretations are from the Linux driver, and don't directly + * match the messages printed by Mylex's BIOS. This may change if + * documentation on the codes is forthcoming. + */ +static int +mlx_fw_message(struct mlx_softc *mlx, int error, int param1, int param2) +{ + const char *fmt; + + switch (error) { + case 0x00: + fmt = "physical drive %d:%d not responding"; + break; + + case 0x08: + /* + * We could be neater about this and give some indication + * when we receive more of them. + */ + if ((mlx->mlx_flags & MLXF_SPINUP_REPORTED) == 0) { + printf("%s: spinning up drives...\n", + mlx->mlx_dv.dv_xname); + mlx->mlx_flags |= MLXF_SPINUP_REPORTED; + } + break; + + case 0x30: + fmt = "configuration checksum error"; + break; + + case 0x60: + fmt = "mirror race recovery failed"; + break; + + case 0x70: + fmt = "mirror race recovery in progress"; + break; + + case 0x90: + fmt = "physical drive %d:%d COD mismatch"; + break; + + case 0xa0: + fmt = "logical drive installation aborted"; + break; + + case 0xb0: + fmt = "mirror race on a critical system drive"; + break; + + case 0xd0: + fmt = "new controller configuration found"; + break; + + case 0xf0: + fmt = "FATAL MEMORY PARITY ERROR"; + return (1); + + default: + printf("%s: unknown firmware init error %02x:%02x:%02x\n", + mlx->mlx_dv.dv_xname, error, param1, param2); + return (0); + } + + printf("%s: ", mlx->mlx_dv.dv_xname); + printf(fmt, param2, param1); + printf("\n"); + + return (0); +} diff --git a/sys/dev/ic/mlxio.h b/sys/dev/ic/mlxio.h new file mode 100644 index 000000000000..43001c8349c1 --- /dev/null +++ b/sys/dev/ic/mlxio.h @@ -0,0 +1,96 @@ +/* $NetBSD: mlxio.h,v 1.1 2001/02/04 17:05:12 ad Exp $ */ + +/*- + * Copyright (c) 1999 Michael Smith + * 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. + * + * 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. + * + * from FreeBSD: mlxio.h,v 1.1.2.2 2000/04/24 19:40:49 msmith Exp + */ + +#ifndef _IC_MLXIO_H_ +#define _IC_MLXIO_H_ + +#include + +/* + * System Disk ioctls + */ + +/* system disk status values */ +#define MLX_SYSD_ONLINE 0x03 +#define MLX_SYSD_CRITICAL 0x04 +#define MLX_SYSD_OFFLINE 0xff + +#define MLXD_STATUS _IOR('M', 100, int) +#define MLXD_CHECKASYNC _IOR('M', 101, int) +#define MLXD_DETACH _IOW('M', 102, int) + +/* + * Controller ioctls + */ +struct mlx_pause { + int mp_which; +#define MLX_PAUSE_ALL 0xff +#define MLX_PAUSE_CANCEL 0x00 + int mp_when; + int mp_howlong; +}; + +struct mlx_usercommand { + size_t mu_datasize; /* size of buffer */ + void *mu_buf; /* user address of buffer */ + int mu_bufptr; /* offset into command m/b for PA */ + int mu_bufdir; /* transfer is to controller */ + u_int16_t mu_status; /* command status returned */ + u_int8_t mu_command[16]; /* command mailbox contents */ +}; +#define MU_XFER_IN 0x01 +#define MU_XFER_OUT 0x02 +#define MU_XFER_MASK 0x03 + +struct mlx_rebuild_request { + int rr_channel; + int rr_target; + int rr_status; +}; + +struct mlx_rebuild_status { + u_int16_t rs_code; +#define MLX_REBUILDSTAT_REBUILDCHECK 0x0000 +#define MLX_REBUILDSTAT_ADDCAPACITY 0x0400 +#define MLX_REBUILDSTAT_ADDCAPACITYINIT 0x0500 +#define MLX_REBUILDSTAT_IDLE 0xffff + u_int16_t rs_drive; + int rs_size; + int rs_remaining; +}; + +#define MLX_RESCAN_DRIVES _IO('M', 0) +#define MLX_PAUSE_CHANNEL _IOW('M', 1, struct mlx_pause) +#define MLX_COMMAND _IOWR('M', 2, struct mlx_usercommand) +#define MLX_REBUILDASYNC _IOWR('M', 3, struct mlx_rebuild_request) +#define MLX_REBUILDSTAT _IOR('M', 4, struct mlx_rebuild_status) +#define MLX_GET_SYSDRIVE _IOWR('M', 5, int) + +#endif /* !_IC_MLXIO_H_ */ diff --git a/sys/dev/ic/mlxreg.h b/sys/dev/ic/mlxreg.h new file mode 100644 index 000000000000..006b1f5b0572 --- /dev/null +++ b/sys/dev/ic/mlxreg.h @@ -0,0 +1,532 @@ +/* $NetBSD: mlxreg.h,v 1.1 2001/02/04 17:05:12 ad Exp $ */ + +/*- + * Copyright (c) 2001 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Andrew Doran. + * + * 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 NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + +/*- + * Copyright (c) 1999 Michael Smith + * 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. + * + * 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. + * + * from FreeBSD: mlxreg.h,v 1.5.2.2 2000/04/24 19:40:50 msmith Exp + */ + +#ifndef _IC_MLXREG_H_ +#define _IC_MLXREG_H_ + +#define MLX_SECTOR_SIZE 512 + +/* + * Selected command codes. + */ +#define MLX_CMD_ENQUIRY_OLD 0x05 +#define MLX_CMD_ENQUIRY 0x53 +#define MLX_CMD_ENQUIRY2 0x1c +#define MLX_CMD_ENQSYSDRIVE 0x19 +#define MLX_CMD_READSG 0xb6 +#define MLX_CMD_WRITESG 0xb7 +#define MLX_CMD_READSG_OLD 0x82 +#define MLX_CMD_WRITESG_OLD 0x83 +#define MLX_CMD_FLUSH 0x0a +#define MLX_CMD_LOGOP 0x72 +#define MLX_CMD_REBUILDASYNC 0x16 +#define MLX_CMD_CHECKASYNC 0x1e +#define MLX_CMD_REBUILDSTAT 0x0c +#define MLX_CMD_STOPCHANNEL 0x13 +#define MLX_CMD_STARTCHANNEL 0x12 +#define MLX_CMD_READ_CONFIG 0x4e +#define MLX_CMD_WRITE_CONFIG 0x4f +#define MLX_CMD_READ_DK_CONFIG 0x4a +#define MLX_CMD_WRITE_DK_CONFIG 0x4b +#define MLX_CMD_DIRECT_CDB 0x04 +#define MLX_CMD_DEVICE_STATE 0x50 +#define MLX_CMD_READ_CONFIG2 0x3d +#define MLX_CMD_WRITE_CONFIG2 0x3c + +#ifdef _KERNEL + +/* + * Status values. + */ +#define MLX_STATUS_OK 0x0000 +#define MLX_STATUS_RDWROFFLINE 0x0002 /* read/write claims drive is offline */ +#define MLX_STATUS_WEDGED 0xdeaf /* controller not listening */ +#define MLX_STATUS_LOST 0xdead /* never came back */ +#define MLX_STATUS_BUSY 0xdeed /* command is in controller */ + +/* + * V1 (EISA) interface. + */ +#define MLX_V1REG_IE 0x09 +#define MLX_V1REG_IDB 0x0d +#define MLX_V1REG_ODB_EN 0x0e +#define MLX_V1REG_ODB 0x0f +#define MLX_V1REG_MAILBOX 0x10 + +#define MLX_V1_IDB_FULL 0x01 /* mailbox is full */ +#define MLX_V1_IDB_INIT_BUSY 0x02 /* init in progress */ + +#define MLX_V1_IDB_SACK 0x02 /* acknowledge status read */ + +#define MLX_V1_ODB_SAVAIL 0x01 /* status is available */ + +#define MLX_V1_FWERROR_PEND 0x04 /* firmware error pending */ + +#define MLX_V1_MAILBOX_LEN 13 + +/* + * V2/V3 interface. + */ +#define MLX_V3REG_MAILBOX 0x00 +#define MLX_V3REG_STATUS_IDENT 0x0d +#define MLX_V3REG_STATUS 0x0e +#define MLX_V3REG_IDB 0x40 +#define MLX_V3REG_ODB 0x41 +#define MLX_V3REG_IE 0x43 +#define MLX_V3REG_FWERROR 0x3f +#define MLX_V3REG_FWERROR_PARAM1 0x00 +#define MLX_V3REG_FWERROR_PARAM2 0x01 + +#define MLX_V3_IDB_FULL 0x01 /* mailbox is full */ +#define MLX_V3_IDB_INIT_BUSY 0x02 /* init in progress */ + +#define MLX_V3_IDB_SACK 0x02 /* acknowledge status read */ + +#define MLX_V3_ODB_SAVAIL 0x01 /* status is available */ + +#define MLX_V3_FWERROR_PEND 0x04 /* firmware error pending */ + +#define MLX_V3_MAILBOX_LEN 13 + +/* + * V4 interface. + */ +#define MLX_V4REG_MAILBOX 0x1000 +#define MLX_V4REG_STATUS_IDENT 0x1018 +#define MLX_V4REG_STATUS 0x101a +#define MLX_V4REG_IDB 0x0020 +#define MLX_V4REG_ODB 0x002c +#define MLX_V4REG_IE 0x0034 +#define MLX_V4REG_FWERROR 0x103f +#define MLX_V4REG_FWERROR_PARAM1 0x1000 +#define MLX_V4REG_FWERROR_PARAM2 0x1001 + +#define MLX_V4_IDB_FULL 0x01 /* mailbox is full */ +#define MLX_V4_IDB_INIT_BUSY 0x02 /* initialisation in progress */ + +#define MLX_V4_IDB_HWMBOX_CMD 0x01 /* posted hardware mailbox command */ +#define MLX_V4_IDB_SACK 0x02 /* acknowledge status read */ +#define MLX_V4_IDB_MEMMBOX_CMD 0x10 /* posted memory mailbox command */ + +#define MLX_V4_ODB_HWSAVAIL 0x01 /* status available for hardware m/b */ +#define MLX_V4_ODB_MEMSAVAIL 0x02 /* status available for memory m/b */ + +#define MLX_V4_ODB_HWMBOX_ACK 0x01 /* ack status read from hardware m/b */ +#define MLX_V4_ODB_MEMMBOX_ACK 0x02 /* ack status read from memory m/b */ + +#define MLX_V4_IE_MASK 0xfb /* message unit interrupt mask */ +#define MLX_V4_IE_DISINT 0x04 /* interrupt disable bit */ + +#define MLX_V4_FWERROR_PEND 0x04 /* firmware error pending */ + +#define MLX_V4_MAILBOX_LEN 16 + +/* + * V5 interface. + */ +#define MLX_V5REG_MAILBOX 0x50 +#define MLX_V5REG_STATUS_IDENT 0x5d +#define MLX_V5REG_STATUS 0x5e +#define MLX_V5REG_IDB 0x60 +#define MLX_V5REG_ODB 0x61 +#define MLX_V5REG_IE 0x34 +#define MLX_V5REG_FWERROR 0x63 +#define MLX_V5REG_FWERROR_PARAM1 0x50 +#define MLX_V5REG_FWERROR_PARAM2 0x51 + +#define MLX_V5_IDB_EMPTY 0x01 /* mailbox is empty */ +#define MLX_V5_IDB_INIT_DONE 0x02 /* initialisation has completed */ + +#define MLX_V5_IDB_HWMBOX_CMD 0x01 /* posted hardware mailbox command */ +#define MLX_V5_IDB_SACK 0x02 /* acknowledge status read */ +#define MLX_V5_IDB_RESET 0x08 /* reset request */ +#define MLX_V5_IDB_MEMMBOX_CMD 0x10 /* posted memory mailbox command */ + +#define MLX_V5_ODB_HWSAVAIL 0x01 /* status available for hardware m/b */ +#define MLX_V5_ODB_MEMSAVAIL 0x02 /* status available for memory m/b */ + +#define MLX_V5_ODB_HWMBOX_ACK 0x01 /* ack status read from hardware m/b */ +#define MLX_V5_ODB_MEMMBOX_ACK 0x02 /* ack status read from memory m/b */ + +#define MLX_V5_IE_DISINT 0x04 /* interrupt disable bit */ + +#define MLX_V5_FWERROR_PEND 0x04 /* firmware error pending */ + +#define MLX_V5_MAILBOX_LEN 16 + +#endif /* _KERNEL */ + +/* + * Scatter-gather list format, type 1, kind 00. + */ +struct mlx_sgentry { + u_int32_t sge_addr; + u_int32_t sge_count; +} __attribute__ ((packed)); + +/* + * Command result buffers, as placed in system memory by the controller. + */ +struct mlx_enquiry_old { + u_int8_t me_num_sys_drvs; + u_int8_t me_res1[3]; + u_int32_t me_drvsize[8]; + u_int16_t me_flash_age; + u_int8_t me_status_flags; + u_int8_t me_free_state_change_count; + u_int8_t me_fwminor; + u_int8_t me_fwmajor; + u_int8_t me_rebuild_flag; + u_int8_t me_max_commands; + u_int8_t me_offline_sd_count; + u_int8_t me_res3; + u_int8_t me_critical_sd_count; + u_int8_t me_res4[3]; + u_int8_t me_dead_count; + u_int8_t me_res5; + u_int8_t me_rebuild_count; + u_int8_t me_misc_flags; + struct { + u_int8_t dd_targ; + u_int8_t dd_chan; + } __attribute__ ((packed)) me_dead[20]; +} __attribute__ ((packed)); + +struct mlx_enquiry { + u_int8_t me_num_sys_drvs; + u_int8_t me_res1[3]; + u_int32_t me_drvsize[32]; + u_int16_t me_flash_age; + u_int8_t me_status_flags; +#define MLX_ENQ_SFLAG_DEFWRERR 0x01 /* deferred write error indicator */ +#define MLX_ENQ_SFLAG_BATTLOW 0x02 /* battery low */ + u_int8_t me_res2; + u_int8_t me_fwminor; + u_int8_t me_fwmajor; + u_int8_t me_rebuild_flag; + u_int8_t me_max_commands; + u_int8_t me_offline_sd_count; + u_int8_t me_res3; + u_int16_t me_event_log_seq_num; + u_int8_t me_critical_sd_count; + u_int8_t me_res4[3]; + u_int8_t me_dead_count; + u_int8_t me_res5; + u_int8_t me_rebuild_count; + u_int8_t me_misc_flags; +#define MLX_ENQ_MISC_BBU 0x08 /* battery backup present */ + struct { + u_int8_t dd_targ; + u_int8_t dd_chan; + } __attribute__ ((packed)) me_dead[20]; +} __attribute__ ((packed)); + +struct mlx_enquiry2 { + u_int8_t me_hardware_id[4]; + u_int8_t me_firmware_id[4]; + u_int32_t me_res1; + u_int8_t me_configured_channels; + u_int8_t me_actual_channels; + u_int8_t me_max_targets; + u_int8_t me_max_tags; + u_int8_t me_max_sys_drives; + u_int8_t me_max_arms; + u_int8_t me_max_spans; + u_int8_t me_res2; + u_int32_t me_res3; + u_int32_t me_mem_size; + u_int32_t me_cache_size; + u_int32_t me_flash_size; + u_int32_t me_nvram_size; + u_int16_t me_mem_type; + u_int16_t me_clock_speed; + u_int16_t me_mem_speed; + u_int16_t me_hardware_speed; + u_int8_t me_res4[12]; + u_int16_t me_max_commands; + u_int16_t me_max_sg; + u_int16_t me_max_dp; + u_int16_t me_max_iod; + u_int16_t me_max_comb; + u_int8_t me_latency; + u_int8_t me_res5; + u_int8_t me_scsi_timeout; + u_int8_t me_res6; + u_int16_t me_min_freelines; + u_int8_t me_res7[8]; + u_int8_t me_rate_const; + u_int8_t me_res8[11]; + u_int16_t me_physblk; + u_int16_t me_logblk; + u_int16_t me_maxblk; + u_int16_t me_blocking_factor; + u_int16_t me_cacheline; + u_int8_t me_scsi_cap; + u_int8_t me_res9[5]; + u_int16_t me_firmware_build; + u_int8_t me_fault_mgmt_type; + u_int8_t me_res10; + u_int32_t me_firmware_features; + u_int8_t me_res11[8]; +} __attribute__ ((packed)); + +/* MLX_CMD_ENQSYSDRIVE returns an array of 32 of these. */ +struct mlx_enq_sys_drive { + u_int32_t sd_size; + u_int8_t sd_state; + u_int8_t sd_raidlevel; + u_int16_t sd_res1; +} __attribute__ ((packed)); + +/* + * MLX_CMD_LOGOP/MLX_LOGOP_GET + * + * Bitfields: + * + * 0-4 el_target SCSI target + * 5-7 el_target SCSI channel + * 0-6 el_errorcode error code + * 7-7 el_errorcode validity (?) + * 0-3 el_sense sense key + * 4-4 el_sense reserved + * 5-5 el_sense ILI + * 6-6 el_sense EOM + * 7-7 el_sense filemark + */ +struct mlx_eventlog_entry { + u_int8_t el_type; + u_int8_t el_length; + u_int8_t el_target; + u_int8_t el_lun; + u_int16_t el_seqno; + u_int8_t el_errorcode; + u_int8_t el_segment; + u_int8_t el_sense; + u_int8_t el_information[4]; + u_int8_t el_addsense; + u_int8_t el_csi[4]; + u_int8_t el_asc; + u_int8_t el_asq; + u_int8_t el_res3[12]; +} __attribute__ ((packed)); + +#define MLX_LOGOP_GET 0x00 /* operation codes for MLX_CMD_LOGOP */ +#define MLX_LOGMSG_SENSE 0x00 /* log message contents codes */ + +struct mlx_rebuild_stat { + u_int32_t rb_drive; + u_int32_t rb_size; + u_int32_t rb_remaining; +} __attribute__ ((packed)); + +struct mlx_config { + u_int16_t cf_flags1; +#define MLX_CF2_ACTV_NEG 0x0002 +#define MLX_CF2_NORSTRTRY 0x0080 +#define MLX_CF2_STRGWRK 0x0100 +#define MLX_CF2_HPSUPP 0x0200 +#define MLX_CF2_NODISCN 0x0400 +#define MLX_CF2_ARM 0x2000 +#define MLX_CF2_OFM 0x8000 +#define MLX_CF2_AEMI (MLX_CF2_ARM | MLX_CF2_OFM) + u_int8_t cf_oemid; + u_int8_t cf_oem_model; + u_int8_t cf_physical_sector; + u_int8_t cf_logical_sector; + u_int8_t cf_blockfactor; + u_int8_t cf_flags2; +#define MLX_CF2_READAH 0x01 +#define MLX_CF2_BIOSDLY 0x02 +#define MLX_CF2_REASS1S 0x10 +#define MLX_CF2_FUAENABL 0x40 +#define MLX_CF2_R5ALLS 0x80 + u_int8_t cf_rcrate; + u_int8_t cf_res1; + u_int8_t cf_blocks_per_cache_line; + u_int8_t cf_blocks_per_stripe; + u_int8_t cf_scsi_param_0; + u_int8_t cf_scsi_param_1; + u_int8_t cf_scsi_param_2; + u_int8_t cf_scsi_param_3; + u_int8_t cf_scsi_param_4; + u_int8_t cf_scsi_param_5; + u_int8_t cf_scsi_initiator_id; + u_int8_t cf_res2; + u_int8_t cf_startup_mode; + u_int8_t cf_simultaneous_spinup_devices; + u_int8_t cf_delay_between_spinups; + u_int8_t cf_res3; + u_int16_t cf_checksum; +} __attribute__ ((packed)); + +struct mlx_config2 { + struct mlx_config cf2_cf; + u_int8_t cf2_reserved0[26]; + u_int8_t cf2_flags; +#define MLX_CF2_BIOS_DIS 0x01 +#define MLX_CF2_CDROM_DIS 0x02 +#define MLX_CF2_GEOM_255 0x20 + u_int8_t cf2_reserved1[9]; + u_int16_t cf2_checksum; +} __attribute__ ((__packed__)); + +struct mlx_sys_drv_span { + u_int32_t sp_start_lba; + u_int32_t sp_nblks; + u_int8_t sp_arm[8]; +} __attribute__ ((packed)); + +struct mlx_sys_drv { + u_int8_t sd_status; + u_int8_t sd_ext_status; + u_int8_t sd_mod1; + u_int8_t sd_mod2; + u_int8_t sd_raidlevel; +#define MLX_SYS_DRV_WRITEBACK (1<<7) +#define MLX_SYS_DRV_RAID0 0 +#define MLX_SYS_DRV_RAID1 1 +#define MLX_SYS_DRV_RAID3 3 +#define MLX_SYS_DRV_RAID5 5 +#define MLX_SYS_DRV_RAID6 6 +#define MLX_SYS_DRV_JBOD 7 + u_int8_t sd_valid_arms; + u_int8_t sd_valid_spans; + u_int8_t sd_init_state; +#define MLX_SYS_DRV_INITTED 0x81; + struct mlx_sys_drv_span sd_span[4]; +} __attribute__ ((packed)); + +struct mlx_phys_drv { + u_int8_t pd_flags1; +#define MLX_PHYS_DRV_PRESENT 0x01 + u_int8_t pd_flags2; +#define MLX_PHYS_DRV_OTHER 0x00 +#define MLX_PHYS_DRV_DISK 0x01 +#define MLX_PHYS_DRV_SEQUENTIAL 0x02 +#define MLX_PHYS_DRV_CDROM 0x03 +#define MLX_PHYS_DRV_FAST20 0x08 +#define MLX_PHYS_DRV_SYNC 0x10 +#define MLX_PHYS_DRV_FAST 0x20 +#define MLX_PHYS_DRV_WIDE 0x40 +#define MLX_PHYS_DRV_TAG 0x80 + u_int8_t pd_status; +#define MLX_PHYS_DRV_DEAD 0x00 +#define MLX_PHYS_DRV_WRONLY 0x02 +#define MLX_PHYS_DRV_ONLINE 0x03 +#define MLX_PHYS_DRV_STANDBY 0x10 + u_int8_t pd_res1; + u_int8_t pd_period; + u_int8_t pd_offset; + u_int32_t pd_config_size; +} __attribute__ ((packed)); + +struct mlx_core_cfg { + u_int8_t cc_num_sys_drives; + u_int8_t cc_res1[3]; + struct mlx_sys_drv cc_sys_drives[32]; + struct mlx_phys_drv cc_phys_drives[5 * 16]; +} __attribute__ ((packed)); + +/* + * Bitfields: + * + * 0-3 dcdb_target SCSI target + * 4-7 dcdb_target SCSI channel + * 0-3 dcdb_length CDB length + * 4-7 dcdb_length high 4 bits of `datasize' + */ +struct mlx_dcdb { + u_int8_t dcdb_target; + u_int8_t dcdb_flags; +#define MLX_DCDB_NO_DATA 0x00 +#define MLX_DCDB_DATA_IN 0x01 +#define MLX_DCDB_DATA_OUT 0x02 +#define MLX_DCDB_EARLY_STATUS 0x04 +#define MLX_DCDB_TIMEOUT_10S 0x10 /* This lot is wrong? [ad] */ +#define MLX_DCDB_TIMEOUT_60S 0x20 +#define MLX_DCDB_TIMEOUT_20M 0x30 +#define MLX_DCDB_TIMEOUT_24H 0x40 +#define MLX_DCDB_NO_AUTO_SENSE 0x40 /* XXX ?? */ +#define MLX_DCDB_DISCONNECT 0x80 + u_int16_t dcdb_datasize; + u_int32_t dcdb_physaddr; + u_int8_t dcdb_length; + u_int8_t dcdb_sense_length; + u_int8_t dcdb_cdb[12]; + u_int8_t dcdb_sense[64]; + u_int8_t dcdb_status; + u_int8_t res1; +} __attribute__ ((packed)); + +struct mlx_bbtable_entry { + u_int32_t bbt_block_number; + u_int8_t bbt_extent; + u_int8_t bbt_res1; + u_int8_t bbt_entry_type; + u_int8_t bbt_system_drive; /* high 3 bits reserved */ +} __attribute__ ((packed)); + +#endif /* !_IC_MLXREG_H_ */ diff --git a/sys/dev/ic/mlxvar.h b/sys/dev/ic/mlxvar.h new file mode 100644 index 000000000000..cef4ec944647 --- /dev/null +++ b/sys/dev/ic/mlxvar.h @@ -0,0 +1,373 @@ +/* $NetBSD: mlxvar.h,v 1.1 2001/02/04 17:05:12 ad Exp $ */ + +/*- + * Copyright (c) 2001 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Andrew Doran. + * + * 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 NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + +/*- + * Copyright (c) 1999 Michael Smith + * 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. + * + * 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. + * + * from FreeBSD: mlxvar.h,v 1.5.2.2 2000/04/24 19:40:50 msmith Exp + */ + +#ifndef _IC_MLXVAR_H_ +#define _IC_MLXVAR_H_ + +#include "locators.h" + +/* Older boards allow up to 17 segments and 64kB transfers. */ +#define MLX_MAX_SEGS 17 +#define MLX_MAX_XFER 65536 +#define MLX_SGL_SIZE (sizeof(struct mlx_sgentry) * MLX_MAX_SEGS) + +/* This shouldn't be ajusted lightly... */ +#define MLX_MAX_DRIVES 32 + +/* Maximum queue depth, matching the older controllers. */ +#define MLX_MAX_QUEUECNT 63 + +/* Number of CCBs to reserve for `special' operations. */ +#define MLX_NCCBS_RESERVE 7 + +/* Structure describing a system drive as attached to the controller. */ +struct mlx_sysdrive { + u_int32_t ms_size; + u_short ms_state; + u_short ms_raidlevel; + struct device *ms_dv; +}; + +/* Optional per-CCB context. */ +struct mlx_ccb; +struct mlx_context { + void (*mx_handler)(struct mlx_ccb *); + void *mx_context; + struct device *mx_dv; +}; + +/* Command control block. */ +struct mlx_ccb { + union { + SIMPLEQ_ENTRY(mlx_ccb) simpleq; + SLIST_ENTRY(mlx_ccb) slist; + TAILQ_ENTRY(mlx_ccb) tailq; + } mc_chain; + u_int mc_flags; + u_int mc_status; + u_int mc_ident; + time_t mc_expiry; + u_int mc_nsgent; + u_int mc_xfer_size; + bus_addr_t mc_xfer_phys; + bus_dmamap_t mc_xfer_map; + struct mlx_context mc_mx; + u_int8_t mc_mbox[16]; +}; +#define MC_XFER_IN MU_XFER_IN /* Map describes inbound xfer */ +#define MC_XFER_OUT MU_XFER_OUT /* Map describes outbound xfer */ +#define MC_WAITING 0x0400 /* We have waiters */ + +/* + * Per-controller state. + */ +struct mlx_softc { + struct device mlx_dv; + bus_space_tag_t mlx_iot; + bus_space_handle_t mlx_ioh; + bus_dma_tag_t mlx_dmat; + bus_dmamap_t mlx_dmamap; + void *mlx_ih; + + SLIST_HEAD(, mlx_ccb) mlx_ccb_freelist; + TAILQ_HEAD(, mlx_ccb) mlx_ccb_worklist; + SIMPLEQ_HEAD(, mlx_ccb) mlx_ccb_queue; + struct mlx_ccb *mlx_ccbs; + int mlx_nccbs; + int mlx_nccbs_free; + + caddr_t mlx_sgls; + bus_addr_t mlx_sgls_paddr; + + int (*mlx_submit)(struct mlx_softc *, struct mlx_ccb *); + int (*mlx_findcomplete)(struct mlx_softc *, u_int *, u_int *); + void (*mlx_intaction)(struct mlx_softc *, int); + int (*mlx_fw_handshake)(struct mlx_softc *, int *, int *, int *); + + int mlx_max_queuecnt; + struct mlx_enquiry2 *mlx_enq2; + int mlx_iftype; + + time_t mlx_lastpoll; + u_int mlx_lastevent; + u_int mlx_currevent; + u_int mlx_bg; + struct mlx_rebuild_status mlx_rebuildstat; + struct mlx_pause mlx_pause; + int mlx_flags; + + struct mlx_sysdrive mlx_sysdrive[MLX_MAX_DRIVES]; +}; + +#define MLX_BG_CHECK 1 /* we started a check */ +#define MLX_BG_REBUILD 2 /* we started a rebuild */ +#define MLX_BG_SPONTANEOUS 3 /* it just happened somehow */ + +#define MLXF_SPINUP_REPORTED 0x0001 /* "spinning up drives" displayed */ +#define MLXF_EVENTLOG_BUSY 0x0002 /* currently reading event log */ +#define MLXF_FW_INITTED 0x0004 /* firmware init crap done */ +#define MLXF_PAUSEWORKS 0x0008 /* channel pause works as expected */ +#define MLXF_OPEN 0x0010 /* control device is open */ +#define MLXF_INITOK 0x0020 /* controller initalised OK */ +#define MLXF_PERIODIC_CTLR 0x0040 /* periodic check running */ +#define MLXF_PERIODIC_DRIVE 0x0080 /* periodic check running */ +#define MLXF_PERIODIC_REBUILD 0x0100 /* periodic check running */ +#define MLXF_EISA 0x0200 /* EISA board */ + +struct mlx_attach_args { + int mlxa_unit; +}; + +#define mlxacf_unit cf_loc[MLXCF_UNIT] + +int mlx_flush(struct mlx_softc *, int); +void mlx_init(struct mlx_softc *, const char *); +int mlx_intr(void *); + +int mlx_ccb_alloc(struct mlx_softc *, struct mlx_ccb **, int); +const char *mlx_ccb_diagnose(struct mlx_ccb *); +void mlx_ccb_enqueue(struct mlx_softc *, struct mlx_ccb *); +void mlx_ccb_free(struct mlx_softc *, struct mlx_ccb *); +int mlx_ccb_map(struct mlx_softc *, struct mlx_ccb *, void *, int, int); +int mlx_ccb_poll(struct mlx_softc *, struct mlx_ccb *, int); +void mlx_ccb_unmap(struct mlx_softc *, struct mlx_ccb *); +int mlx_ccb_wait(struct mlx_softc *, struct mlx_ccb *); + +static __inline__ void mlx_make_type1(struct mlx_ccb *, u_int8_t, u_int16_t, + u_int32_t, u_int8_t, u_int32_t, + u_int8_t); +static __inline__ void mlx_make_type2(struct mlx_ccb *, u_int8_t, u_int8_t, + u_int8_t, u_int8_t, u_int8_t, + u_int8_t, u_int8_t, u_int32_t, + u_int8_t); +static __inline__ void mlx_make_type3(struct mlx_ccb *, u_int8_t, u_int8_t, + u_int8_t, u_int16_t, u_int8_t, + u_int8_t, u_int32_t, u_int8_t); +static __inline__ void mlx_make_type4(struct mlx_ccb *, u_int8_t, u_int16_t, + u_int32_t, u_int32_t, u_int8_t); +static __inline__ void mlx_make_type5(struct mlx_ccb *, u_int8_t, u_int8_t, + u_int8_t, u_int32_t, u_int32_t, + u_int8_t); + +static __inline__ u_int8_t mlx_inb(struct mlx_softc *, int); +static __inline__ u_int16_t mlx_inw(struct mlx_softc *, int); +static __inline__ u_int32_t mlx_inl(struct mlx_softc *, int); +static __inline__ void mlx_outb(struct mlx_softc *, int, u_int8_t); +static __inline__ void mlx_outw(struct mlx_softc *, int, u_int16_t); +static __inline__ void mlx_outl(struct mlx_softc *, int, u_int32_t); + +static __inline__ void +mlx_make_type1(struct mlx_ccb *mc, u_int8_t code, u_int16_t f1, u_int32_t f2, + u_int8_t f3, u_int32_t f4, u_int8_t f5) +{ + + mc->mc_mbox[0x0] = code; + mc->mc_mbox[0x2] = f1; + mc->mc_mbox[0x3] = (((f2 >> 24) & 0x3) << 6) | ((f1 >> 8) & 0x3f); + mc->mc_mbox[0x4] = f2; + mc->mc_mbox[0x5] = (f2 >> 8); + mc->mc_mbox[0x6] = (f2 >> 16); + mc->mc_mbox[0x7] = f3; + mc->mc_mbox[0x8] = f4; + mc->mc_mbox[0x9] = (f4 >> 8); + mc->mc_mbox[0xa] = (f4 >> 16); + mc->mc_mbox[0xb] = (f4 >> 24); + mc->mc_mbox[0xc] = f5; +} + +static __inline__ void +mlx_make_type2(struct mlx_ccb *mc, u_int8_t code, u_int8_t f1, u_int8_t f2, + u_int8_t f3, u_int8_t f4, u_int8_t f5, u_int8_t f6, + u_int32_t f7, u_int8_t f8) +{ + + mc->mc_mbox[0x0] = code; + mc->mc_mbox[0x2] = f1; + mc->mc_mbox[0x3] = f2; + mc->mc_mbox[0x4] = f3; + mc->mc_mbox[0x5] = f4; + mc->mc_mbox[0x6] = f5; + mc->mc_mbox[0x7] = f6; + mc->mc_mbox[0x8] = f7; + mc->mc_mbox[0x9] = (f7 >> 8); + mc->mc_mbox[0xa] = (f7 >> 16); + mc->mc_mbox[0xb] = (f7 >> 24); + mc->mc_mbox[0xc] = f8; +} + +static __inline__ void +mlx_make_type3(struct mlx_ccb *mc, u_int8_t code, u_int8_t f1, u_int8_t f2, + u_int16_t f3, u_int8_t f4, u_int8_t f5, u_int32_t f6, + u_int8_t f7) +{ + + mc->mc_mbox[0x0] = code; + mc->mc_mbox[0x2] = f1; + mc->mc_mbox[0x3] = f2; + mc->mc_mbox[0x4] = f3; + mc->mc_mbox[0x5] = (f3 >> 8); + mc->mc_mbox[0x6] = f4; + mc->mc_mbox[0x7] = f5; + mc->mc_mbox[0x8] = f6; + mc->mc_mbox[0x9] = (f6 >> 8); + mc->mc_mbox[0xa] = (f6 >> 16); + mc->mc_mbox[0xb] = (f6 >> 24); + mc->mc_mbox[0xc] = f7; +} + +static __inline__ void +mlx_make_type4(struct mlx_ccb *mc, u_int8_t code, u_int16_t f1, u_int32_t f2, + u_int32_t f3, u_int8_t f4) +{ + + mc->mc_mbox[0x0] = code; + mc->mc_mbox[0x2] = f1; + mc->mc_mbox[0x3] = (f1 >> 8); + mc->mc_mbox[0x4] = f2; + mc->mc_mbox[0x5] = (f2 >> 8); + mc->mc_mbox[0x6] = (f2 >> 16); + mc->mc_mbox[0x7] = (f2 >> 24); + mc->mc_mbox[0x8] = f3; + mc->mc_mbox[0x9] = (f3 >> 8); + mc->mc_mbox[0xa] = (f3 >> 16); + mc->mc_mbox[0xb] = (f3 >> 24); + mc->mc_mbox[0xc] = f4; +} + +static __inline__ void +mlx_make_type5(struct mlx_ccb *mc, u_int8_t code, u_int8_t f1, u_int8_t f2, + u_int32_t f3, u_int32_t f4, u_int8_t f5) +{ + + mc->mc_mbox[0x0] = code; + mc->mc_mbox[0x2] = f1; + mc->mc_mbox[0x3] = f2; + mc->mc_mbox[0x4] = f3; + mc->mc_mbox[0x5] = (f3 >> 8); + mc->mc_mbox[0x6] = (f3 >> 16); + mc->mc_mbox[0x7] = (f3 >> 24); + mc->mc_mbox[0x8] = f4; + mc->mc_mbox[0x9] = (f4 >> 8); + mc->mc_mbox[0xa] = (f4 >> 16); + mc->mc_mbox[0xb] = (f4 >> 24); + mc->mc_mbox[0xc] = f5; +} + +static __inline__ u_int8_t +mlx_inb(struct mlx_softc *mlx, int off) +{ + + bus_space_barrier(mlx->mlx_iot, mlx->mlx_ioh, off, 1, + BUS_SPACE_BARRIER_WRITE | BUS_SPACE_BARRIER_READ); + return (bus_space_read_1(mlx->mlx_iot, mlx->mlx_ioh, off)); +} + +static __inline__ u_int16_t +mlx_inw(struct mlx_softc *mlx, int off) +{ + + bus_space_barrier(mlx->mlx_iot, mlx->mlx_ioh, off, 2, + BUS_SPACE_BARRIER_WRITE | BUS_SPACE_BARRIER_READ); + return (bus_space_read_2(mlx->mlx_iot, mlx->mlx_ioh, off)); +} + +static __inline__ u_int32_t +mlx_inl(struct mlx_softc *mlx, int off) +{ + + bus_space_barrier(mlx->mlx_iot, mlx->mlx_ioh, off, 4, + BUS_SPACE_BARRIER_WRITE | BUS_SPACE_BARRIER_READ); + return (bus_space_read_4(mlx->mlx_iot, mlx->mlx_ioh, off)); +} + +static __inline__ void +mlx_outb(struct mlx_softc *mlx, int off, u_int8_t val) +{ + + bus_space_write_1(mlx->mlx_iot, mlx->mlx_ioh, off, val); + bus_space_barrier(mlx->mlx_iot, mlx->mlx_ioh, off, 1, + BUS_SPACE_BARRIER_WRITE); +} + +static __inline__ void +mlx_outw(struct mlx_softc *mlx, int off, u_int16_t val) +{ + + bus_space_write_2(mlx->mlx_iot, mlx->mlx_ioh, off, val); + bus_space_barrier(mlx->mlx_iot, mlx->mlx_ioh, off, 2, + BUS_SPACE_BARRIER_WRITE); +} + +static __inline__ void +mlx_outl(struct mlx_softc *mlx, int off, u_int32_t val) +{ + + bus_space_write_4(mlx->mlx_iot, mlx->mlx_ioh, off, val); + bus_space_barrier(mlx->mlx_iot, mlx->mlx_ioh, off, 4, + BUS_SPACE_BARRIER_WRITE); +} + +#endif /* !_IC_MLXVAR_H_ */ diff --git a/sys/dev/pci/files.pci b/sys/dev/pci/files.pci index aa0d27b197df..2cdd7d6c198b 100644 --- a/sys/dev/pci/files.pci +++ b/sys/dev/pci/files.pci @@ -1,4 +1,4 @@ -# $NetBSD: files.pci,v 1.118 2001/01/22 17:40:14 ad Exp $ +# $NetBSD: files.pci,v 1.119 2001/02/04 17:05:12 ad Exp $ # # Config file and device description for machine-independent PCI code. # Included by ports that need it. Requires that the SCSI files be @@ -53,6 +53,10 @@ file dev/pci/ld_twe.c ld_twe attach cac at pci with cac_pci file dev/pci/cac_pci.c cac_pci +# Mylex RAID controllers +attach mlx at pci with mlx_pci +file dev/pci/mlx_pci.c mlx_pci + # DPT EATA SCSI controllers attach dpt at pci with dpt_pci file dev/pci/dpt_pci.c dpt_pci diff --git a/sys/dev/pci/mlx_pci.c b/sys/dev/pci/mlx_pci.c new file mode 100644 index 000000000000..e39badcb7820 --- /dev/null +++ b/sys/dev/pci/mlx_pci.c @@ -0,0 +1,619 @@ +/* $NetBSD: mlx_pci.c,v 1.1 2001/02/04 17:05:12 ad Exp $ */ + +/*- + * Copyright (c) 2001 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Andrew Doran. + * + * 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 NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + +/*- + * Copyright (c) 1999 Michael Smith + * 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. + * + * 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. + * + * from FreeBSD: mlx_pci.c,v 1.4.2.4 2000/10/28 10:48:09 msmith Exp + */ + +/* + * PCI front-end for the mlx(4) driver. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include +#include +#include + +static void mlx_pci_attach(struct device *, struct device *, void *); +static int mlx_pci_match(struct device *, struct cfdata *, void *); +static const struct mlx_pci_ident *mlx_pci_findmpi(struct pci_attach_args *); + +static int mlx_v3_submit(struct mlx_softc *, struct mlx_ccb *); +static int mlx_v3_findcomplete(struct mlx_softc *, u_int *, u_int *); +static void mlx_v3_intaction(struct mlx_softc *, int); +static int mlx_v3_fw_handshake(struct mlx_softc *, int *, int *, int *); + +static int mlx_v4_submit(struct mlx_softc *, struct mlx_ccb *); +static int mlx_v4_findcomplete(struct mlx_softc *, u_int *, u_int *); +static void mlx_v4_intaction(struct mlx_softc *, int); +static int mlx_v4_fw_handshake(struct mlx_softc *, int *, int *, int *); + +static int mlx_v5_submit(struct mlx_softc *, struct mlx_ccb *); +static int mlx_v5_findcomplete(struct mlx_softc *, u_int *, u_int *); +static void mlx_v5_intaction(struct mlx_softc *, int); +static int mlx_v5_fw_handshake(struct mlx_softc *, int *, int *, int *); + +struct mlx_pci_ident { + u_short mpi_vendor; + u_short mpi_product; + u_short mpi_subvendor; + u_short mpi_subproduct; + int mpi_iftype; +} static const mlx_pci_ident[] = { + { + PCI_VENDOR_MYLEX, + PCI_PRODUCT_MYLEX_RAID_V2, + 0x0000, + 0x0000, + 2, + }, + { + PCI_VENDOR_MYLEX, + PCI_PRODUCT_MYLEX_RAID_V3, + 0x0000, + 0x0000, + 3, + }, + { + PCI_VENDOR_MYLEX, + PCI_PRODUCT_MYLEX_RAID_V4, + 0x0000, + 0x0000, + 4, + }, + { + PCI_VENDOR_DEC, + PCI_PRODUCT_DEC_SWXCR, + PCI_VENDOR_MYLEX, + PCI_PRODUCT_MYLEX_RAID_V5, + 5, + }, +}; + +struct cfattach mlx_pci_ca = { + sizeof(struct mlx_softc), mlx_pci_match, mlx_pci_attach +}; + +/* + * Try to find a `mlx_pci_ident' entry corresponding to this board. + */ +static const struct mlx_pci_ident * +mlx_pci_findmpi(struct pci_attach_args *pa) +{ + const struct mlx_pci_ident *mpi, *maxmpi; + pcireg_t reg; + + mpi = mlx_pci_ident; + maxmpi = mpi + sizeof(mlx_pci_ident) / sizeof(mlx_pci_ident[0]); + + for (; mpi < maxmpi; mpi++) { + if (PCI_VENDOR(pa->pa_id) != mpi->mpi_vendor || + PCI_PRODUCT(pa->pa_id) != mpi->mpi_product) + continue; + + if (mpi->mpi_subvendor == 0x0000) + return (mpi); + + reg = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_SUBSYS_ID_REG); + + if (PCI_VENDOR(reg) == mpi->mpi_subvendor && + PCI_PRODUCT(reg) == mpi->mpi_subproduct) + return (mpi); + } + + return (NULL); +} + +/* + * Match a supported board. + */ +static int +mlx_pci_match(struct device *parent, struct cfdata *cfdata, void *aux) +{ + + return (mlx_pci_findmpi(aux) != NULL); +} + +/* + * Attach a supported board. + */ +static void +mlx_pci_attach(struct device *parent, struct device *self, void *aux) +{ + struct pci_attach_args *pa; + struct mlx_softc *mlx; + pci_chipset_tag_t pc; + pci_intr_handle_t ih; + pcireg_t reg; + const char *intrstr; + int ior, memr, i; + const struct mlx_pci_ident *mpi; + + mlx = (struct mlx_softc *)self; + pa = aux; + pc = pa->pa_pc; + mpi = mlx_pci_findmpi(aux); + + mlx->mlx_dmat = pa->pa_dmat; + mlx->mlx_iftype = mpi->mpi_iftype; + + printf(": Mylex RAID (v%d interface)\n", mpi->mpi_iftype); + + /* + * Map the PCI register window. + */ + memr = -1; + ior = -1; + + for (i = 0x10; i <= 0x14; i += 4) { + reg = pci_conf_read(pa->pa_pc, pa->pa_tag, i); + + if (PCI_MAPREG_TYPE(reg) == PCI_MAPREG_TYPE_IO) { + if (ior == -1 && PCI_MAPREG_IO_SIZE(reg) != 0) + ior = i; + } else { + if (memr == -1 && PCI_MAPREG_MEM_SIZE(reg) != 0) + memr = i; + } + } + + if (memr != -1) + if (pci_mapreg_map(pa, memr, PCI_MAPREG_TYPE_MEM, 0, + &mlx->mlx_iot, &mlx->mlx_ioh, NULL, NULL)) + memr = -1; + if (ior != -1) + if (pci_mapreg_map(pa, ior, PCI_MAPREG_TYPE_IO, 0, + &mlx->mlx_iot, &mlx->mlx_ioh, NULL, NULL)) + ior = -1; + if (memr == -1 && ior == -1) { + printf("%s: can't map i/o or memory space\n", self->dv_xname); + return; + } + + /* Enable the device. */ + reg = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_COMMAND_STATUS_REG); + pci_conf_write(pa->pa_pc, pa->pa_tag, PCI_COMMAND_STATUS_REG, + reg | PCI_COMMAND_MASTER_ENABLE); + + /* Map and establish the interrupt. */ + if (pci_intr_map(pa, &ih)) { + printf("%s: can't map interrupt\n", self->dv_xname); + return; + } + intrstr = pci_intr_string(pc, ih); + mlx->mlx_ih = pci_intr_establish(pc, ih, IPL_BIO, mlx_intr, mlx); + if (mlx->mlx_ih == NULL) { + printf("%s: can't establish interrupt", self->dv_xname); + if (intrstr != NULL) + printf(" at %s", intrstr); + printf("\n"); + return; + } + + /* Select linkage based on controller interface type. */ + switch (mlx->mlx_iftype) { + case 2: + case 3: + mlx->mlx_submit = mlx_v3_submit; + mlx->mlx_findcomplete = mlx_v3_findcomplete; + mlx->mlx_intaction = mlx_v3_intaction; + mlx->mlx_fw_handshake = mlx_v3_fw_handshake; + break; + + case 4: + mlx->mlx_submit = mlx_v4_submit; + mlx->mlx_findcomplete = mlx_v4_findcomplete; + mlx->mlx_intaction = mlx_v4_intaction; + mlx->mlx_fw_handshake = mlx_v4_fw_handshake; + break; + + case 5: + mlx->mlx_submit = mlx_v5_submit; + mlx->mlx_findcomplete = mlx_v5_findcomplete; + mlx->mlx_intaction = mlx_v5_intaction; + mlx->mlx_fw_handshake = mlx_v5_fw_handshake; + break; + } + + mlx_init(mlx, intrstr); +} + +/* + * ================= V3 interface linkage ================= + */ + +/* + * Try to give (mc) to the controller. Returns 1 if successful, 0 on + * failure (the controller is not ready to take a command). + * + * Must be called at splbio or in a fashion that prevents reentry. + */ +static int +mlx_v3_submit(struct mlx_softc *mlx, struct mlx_ccb *mc) +{ + + /* Ready for our command? */ + if ((mlx_inb(mlx, MLX_V3REG_IDB) & MLX_V3_IDB_FULL) == 0) { + /* Copy mailbox data to window. */ + bus_space_write_region_1(mlx->mlx_iot, mlx->mlx_ioh, + MLX_V3REG_MAILBOX, mc->mc_mbox, MLX_V3_MAILBOX_LEN); + bus_space_barrier(mlx->mlx_iot, mlx->mlx_ioh, + MLX_V3REG_MAILBOX, MLX_V3_MAILBOX_LEN, + BUS_SPACE_BARRIER_WRITE); + + /* Post command. */ + mlx_outb(mlx, MLX_V3REG_IDB, MLX_V3_IDB_FULL); + return (1); + } + + return (0); +} + +/* + * See if a command has been completed, if so acknowledge its completion and + * recover the slot number and status code. + * + * Must be called at splbio or in a fashion that prevents reentry. + */ +static int +mlx_v3_findcomplete(struct mlx_softc *mlx, u_int *slot, u_int *status) +{ + + /* Status available? */ + if ((mlx_inb(mlx, MLX_V3REG_ODB) & MLX_V3_ODB_SAVAIL) != 0) { + *slot = mlx_inb(mlx, MLX_V3REG_STATUS_IDENT); + *status = mlx_inw(mlx, MLX_V3REG_STATUS); + + /* Acknowledge completion. */ + mlx_outb(mlx, MLX_V3REG_ODB, MLX_V3_ODB_SAVAIL); + mlx_outb(mlx, MLX_V3REG_IDB, MLX_V3_IDB_SACK); + return (1); + } + + return (0); +} + +/* + * Enable/disable interrupts as requested. (No acknowledge required) + * + * Must be called at splbio or in a fashion that prevents reentry. + */ +static void +mlx_v3_intaction(struct mlx_softc *mlx, int action) +{ + + mlx_outb(mlx, MLX_V3REG_IE, action != 0); +} + +/* + * Poll for firmware error codes during controller initialisation. + * + * Returns 0 if initialisation is complete, 1 if still in progress but no + * error has been fetched, 2 if an error has been retrieved. + */ +static int +mlx_v3_fw_handshake(struct mlx_softc *mlx, int *error, int *param1, int *param2) +{ + u_int8_t fwerror; + + /* First time around, clear any hardware completion status. */ + if ((mlx->mlx_flags & MLXF_FW_INITTED) == 0) { + mlx_outb(mlx, MLX_V3REG_IDB, MLX_V3_IDB_SACK); + DELAY(1000); + mlx->mlx_flags |= MLXF_FW_INITTED; + } + + /* Init in progress? */ + if ((mlx_inb(mlx, MLX_V3REG_IDB) & MLX_V3_IDB_INIT_BUSY) == 0) + return (0); + + /* Test error value. */ + fwerror = mlx_inb(mlx, MLX_V3REG_FWERROR); + + if ((fwerror & MLX_V3_FWERROR_PEND) == 0) + return (1); + + /* Mask status pending bit, fetch status. */ + *error = fwerror & ~MLX_V3_FWERROR_PEND; + *param1 = mlx_inb(mlx, MLX_V3REG_FWERROR_PARAM1); + *param2 = mlx_inb(mlx, MLX_V3REG_FWERROR_PARAM2); + + /* Acknowledge. */ + mlx_outb(mlx, MLX_V3REG_FWERROR, 0); + + return (2); +} + +/* + * ================= V4 interface linkage ================= + */ + +/* + * Try to give (mc) to the controller. Returns 1 if successful, 0 on + * failure (the controller is not ready to take a command). + * + * Must be called at splbio or in a fashion that prevents reentry. + */ +static int +mlx_v4_submit(struct mlx_softc *mlx, struct mlx_ccb *mc) +{ + + /* Ready for our command? */ + if ((mlx_inl(mlx, MLX_V4REG_IDB) & MLX_V4_IDB_FULL) == 0) { + /* Copy mailbox data to window. */ + bus_space_write_region_4(mlx->mlx_iot, mlx->mlx_ioh, + MLX_V4REG_MAILBOX, mc->mc_mbox, MLX_V4_MAILBOX_LEN >> 2); + bus_space_barrier(mlx->mlx_iot, mlx->mlx_ioh, + MLX_V4REG_MAILBOX, MLX_V4_MAILBOX_LEN, + BUS_SPACE_BARRIER_WRITE); + + /* Post command. */ + mlx_outl(mlx, MLX_V4REG_IDB, MLX_V4_IDB_HWMBOX_CMD); + return (1); + } + + return (0); +} + +/* + * See if a command has been completed, if so acknowledge its completion and + * recover the slot number and status code. + * + * Must be called at splbio or in a fashion that prevents reentry. + */ +static int +mlx_v4_findcomplete(struct mlx_softc *mlx, u_int *slot, u_int *status) +{ + + /* Status available? */ + if ((mlx_inl(mlx, MLX_V4REG_ODB) & MLX_V4_ODB_HWSAVAIL) != 0) { + *slot = mlx_inb(mlx, MLX_V4REG_STATUS_IDENT); + *status = mlx_inw(mlx, MLX_V4REG_STATUS); + + /* Acknowledge completion. */ + mlx_outl(mlx, MLX_V4REG_ODB, MLX_V4_ODB_HWMBOX_ACK); + mlx_outl(mlx, MLX_V4REG_IDB, MLX_V4_IDB_SACK); + return (1); + } + + return (0); +} + +/* + * Enable/disable interrupts as requested. + * + * Must be called at splbio or in a fashion that prevents reentry. + */ +static void +mlx_v4_intaction(struct mlx_softc *mlx, int action) +{ + u_int32_t ier; + + if (!action) + ier = MLX_V4_IE_MASK | MLX_V4_IE_DISINT; + else + ier = MLX_V4_IE_MASK & ~MLX_V4_IE_DISINT; + + mlx_outl(mlx, MLX_V4REG_IE, ier); +} + +/* + * Poll for firmware error codes during controller initialisation. + * + * Returns 0 if initialisation is complete, 1 if still in progress but no + * error has been fetched, 2 if an error has been retrieved. + */ +static int +mlx_v4_fw_handshake(struct mlx_softc *mlx, int *error, int *param1, int *param2) +{ + u_int8_t fwerror; + + /* First time around, clear any hardware completion status. */ + if ((mlx->mlx_flags & MLXF_FW_INITTED) == 0) { + mlx_outl(mlx, MLX_V4REG_IDB, MLX_V4_IDB_SACK); + DELAY(1000); + mlx->mlx_flags |= MLXF_FW_INITTED; + } + + /* Init in progress? */ + if ((mlx_inl(mlx, MLX_V4REG_IDB) & MLX_V4_IDB_INIT_BUSY) == 0) + return (0); + + /* Test error value */ + fwerror = mlx_inb(mlx, MLX_V4REG_FWERROR); + if ((fwerror & MLX_V4_FWERROR_PEND) == 0) + return (1); + + /* Mask status pending bit, fetch status. */ + *error = fwerror & ~MLX_V4_FWERROR_PEND; + *param1 = mlx_inb(mlx, MLX_V4REG_FWERROR_PARAM1); + *param2 = mlx_inb(mlx, MLX_V4REG_FWERROR_PARAM2); + + /* Acknowledge. */ + mlx_outb(mlx, MLX_V4REG_FWERROR, 0); + + return (2); +} + +/* + * ================= V5 interface linkage ================= + */ + +/* + * Try to give (mc) to the controller. Returns 1 if successful, 0 on failure + * (the controller is not ready to take a command). + * + * Must be called at splbio or in a fashion that prevents reentry. + */ +static int +mlx_v5_submit(struct mlx_softc *mlx, struct mlx_ccb *mc) +{ + + /* Ready for our command? */ + if ((mlx_inb(mlx, MLX_V5REG_IDB) & MLX_V5_IDB_EMPTY) != 0) { + /* Copy mailbox data to window. */ + bus_space_write_region_4(mlx->mlx_iot, mlx->mlx_ioh, + MLX_V5REG_MAILBOX, mc->mc_mbox, MLX_V5_MAILBOX_LEN >> 2); + bus_space_barrier(mlx->mlx_iot, mlx->mlx_ioh, + MLX_V5REG_MAILBOX, MLX_V5_MAILBOX_LEN, + BUS_SPACE_BARRIER_WRITE); + + /* Post command */ + mlx_outb(mlx, MLX_V5REG_IDB, MLX_V5_IDB_HWMBOX_CMD); + return (1); + } + + return (0); +} + +/* + * See if a command has been completed, if so acknowledge its completion and + * recover the slot number and status code. + * + * Must be called at splbio or in a fashion that prevents reentry. + */ +static int +mlx_v5_findcomplete(struct mlx_softc *mlx, u_int *slot, u_int *status) +{ + + /* Status available? */ + if ((mlx_inb(mlx, MLX_V5REG_ODB) & MLX_V5_ODB_HWSAVAIL) != 0) { + *slot = mlx_inb(mlx, MLX_V5REG_STATUS_IDENT); + *status = mlx_inw(mlx, MLX_V5REG_STATUS); + + /* Acknowledge completion. */ + mlx_outb(mlx, MLX_V5REG_ODB, MLX_V5_ODB_HWMBOX_ACK); + mlx_outb(mlx, MLX_V5REG_IDB, MLX_V5_IDB_SACK); + return (1); + } + + return (0); +} + +/* + * Enable/disable interrupts as requested. + * + * Must be called at splbio or in a fashion that prevents reentry. + */ +static void +mlx_v5_intaction(struct mlx_softc *mlx, int action) +{ + u_int8_t ier; + + if (!action) + ier = 0xff & MLX_V5_IE_DISINT; + else + ier = 0xff & ~MLX_V5_IE_DISINT; + + mlx_outb(mlx, MLX_V5REG_IE, ier); +} + +/* + * Poll for firmware error codes during controller initialisation. + * + * Returns 0 if initialisation is complete, 1 if still in progress but no + * error has been fetched, 2 if an error has been retrieved. + */ +static int +mlx_v5_fw_handshake(struct mlx_softc *mlx, int *error, int *param1, int *param2) +{ + u_int8_t fwerror; + + /* First time around, clear any hardware completion status. */ + if ((mlx->mlx_flags & MLXF_FW_INITTED) == 0) { + mlx_outb(mlx, MLX_V5REG_IDB, MLX_V5_IDB_SACK); + DELAY(1000); + mlx->mlx_flags |= MLXF_FW_INITTED; + } + + /* Init in progress? */ + if ((mlx_inb(mlx, MLX_V5REG_IDB) & MLX_V5_IDB_INIT_DONE) != 0) + return (0); + + /* Test for error value. */ + fwerror = mlx_inb(mlx, MLX_V5REG_FWERROR); + if ((fwerror & MLX_V5_FWERROR_PEND) == 0) + return (1); + + /* Mask status pending bit, fetch status. */ + *error = fwerror & ~MLX_V5_FWERROR_PEND; + *param1 = mlx_inb(mlx, MLX_V5REG_FWERROR_PARAM1); + *param2 = mlx_inb(mlx, MLX_V5REG_FWERROR_PARAM2); + + /* Acknowledge. */ + mlx_outb(mlx, MLX_V5REG_FWERROR, 0xff); + + return (2); +}