Driver for Compaq array controllers and disks (cac(4)/ca(4)).

This commit is contained in:
ad 2000-03-16 14:52:22 +00:00
parent 415cbeb83f
commit 70063dc527
8 changed files with 1801 additions and 3 deletions

View File

@ -1,4 +1,4 @@
# $NetBSD: files,v 1.354 2000/03/15 02:10:09 fvdl Exp $
# $NetBSD: files,v 1.355 2000/03/16 14:52:23 ad Exp $
# @(#)files.newconf 7.5 (Berkeley) 5/10/93
@ -211,6 +211,14 @@ file dev/ic/aic77xx.c ahc_aic77xx
device dpt: scsi
file dev/ic/dpt.c dpt
# Compaq Smart ARRAY controllers
device cac {unit = -1}
file dev/ic/cac.c cac
device ca: disk
attach ca at cac
file dev/ic/ca.c ca needs-flag
# AdvanSys 1200A, 1200B and ULTRA SCSI controllers
device adv: scsi
file dev/ic/adv.c adv

701
sys/dev/ic/ca.c Normal file
View File

@ -0,0 +1,701 @@
/* $NetBSD: ca.c,v 1.1 2000/03/16 14:52:23 ad Exp $ */
/*-
* Copyright (c) 1998, 2000 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Charles M. Hannum and Andy 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.
*/
/*
* Originally written by Julian Elischer (julian@dialix.oz.au)
* for TRW Financial Systems for use under the MACH(2.5) operating system.
*
* TRW Financial Systems, in accordance with their agreement with Carnegie
* Mellon University, makes this software available to CMU to distribute
* or use in any manner that they see fit as long as this message is kept with
* the software. For this reason TFS also grants any other persons or
* organisations permission to use or modify this software.
*
* TFS supplies this software to be publicly redistributed
* on the understanding that TFS is not responsible for the correct
* functioning of this software in any circumstances.
*
* Ported to run under 386BSD by Julian Elischer (julian@dialix.oz.au) Sept 1992
*/
/*
* Disk driver for Compaq arrays, based on sd.c (revision 1.157).
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: ca.c,v 1.1 2000/03/16 14:52:23 ad Exp $");
#include "rnd.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/device.h>
#include <sys/queue.h>
#include <sys/proc.h>
#include <sys/buf.h>
#include <sys/endian.h>
#include <sys/disklabel.h>
#include <sys/disk.h>
#include <sys/dkio.h>
#include <sys/stat.h>
#include <sys/lock.h>
#include <sys/conf.h>
#include <sys/fcntl.h>
#if NRND > 0
#include <sys/rnd.h>
#endif
#include <machine/bswap.h>
#include <machine/bus.h>
#include <dev/ic/cacreg.h>
#include <dev/ic/cacvar.h>
#define CAUNIT(dev) DISKUNIT(dev)
#define CAPART(dev) DISKPART(dev)
#define CAMINOR(unit, part) DISKMINOR(unit, part)
#define CAMAKEDEV(maj, unit, part) MAKEDISKDEV(maj, unit, part)
#define CALABELDEV(dev) (CAMAKEDEV(major(dev), CAUNIT(dev), RAW_PART))
struct ca_softc {
struct device sc_dv;
int sc_unit;
int sc_flags;
struct cac_softc *sc_cac;
struct disk sc_dk;
#if NRND > 0
rndsource_element_t sc_rnd_source;
#endif
/* Parameters from controller. */
int sc_ncylinders;
int sc_nheads;
int sc_nsectors;
int sc_secsize;
int sc_secperunit;
int sc_mirror;
};
#define CAF_ENABLED 0x01 /* device enabled */
#define CAF_LOCKED 0x02 /* lock held */
#define CAF_WANTED 0x04 /* lock wanted */
#define CAF_WLABEL 0x08 /* label is writable */
#define CAF_LABELLING 0x10 /* writing label */
static int calock __P((struct ca_softc *));
static void caunlock __P((struct ca_softc *));
static int camatch __P((struct device *, struct cfdata *, void *));
static void cacttach __P((struct device *, struct device *, void *));
static void cadone __P((struct cac_ccb *, int));
static void cagetdisklabel __P((struct ca_softc *));
static void cagetdefaultlabel __P((struct ca_softc *, struct disklabel *));
struct cfattach ca_ca = {
sizeof(struct ca_softc), camatch, cacttach
};
extern struct cfdriver ca_cd;
struct dkdriver cadkdriver = { castrategy };
static int
camatch(parent, match, aux)
struct device *parent;
struct cfdata *match;
void *aux;
{
#if 0
struct cac_attach_args *caca;
caca = (struct cac_attach_args *)aux;
/* Unit 0 is the controller */
return (caca->caca_unit != 0);
#endif
return (1);
}
static void
cacttach(parent, self, aux)
struct device *parent;
struct device *self;
void *aux;
{
struct cac_drive_info dinfo;
struct cac_attach_args *caca;
struct ca_softc *sc;
char *type;
int mb;
sc = (struct ca_softc *)self;
caca = (struct cac_attach_args *)aux;
sc->sc_cac = (struct cac_softc *)parent;
sc->sc_unit = caca->caca_unit;
if (cac_cmd(sc->sc_cac, CAC_CMD_GET_LOG_DRV_INFO, &dinfo, sizeof(dinfo),
sc->sc_unit, 0, CAC_CCB_DATA_IN, NULL)) {
printf("%s: CMD_GET_LOG_DRV_INFO failed\n",
sc->sc_dv.dv_xname);
return;
}
sc->sc_ncylinders = CAC_GET2(dinfo.ncylinders);
sc->sc_nheads = CAC_GET1(dinfo.nheads);
sc->sc_nsectors = CAC_GET1(dinfo.nsectors);
sc->sc_secsize = CAC_GET2(dinfo.secsize);
sc->sc_mirror = CAC_GET1(dinfo.mirror);
sc->sc_secperunit = sc->sc_ncylinders * sc->sc_nheads * sc->sc_nsectors;
switch (sc->sc_mirror) {
case 0:
type = "standalone disk or RAID0";
break;
case 1:
type = "RAID4";
break;
case 2:
type = "RAID1";
break;
case 3:
type = "RAID5";
break;
default:
type = "unknown type of";
break;
}
printf(": %s array\n", type);
mb = sc->sc_secperunit / (1048576 / sc->sc_secsize);
printf("%s: %uMB, %u cyl, %u head, %u sec, %d bytes/sect "
"x %d sectors\n", sc->sc_dv.dv_xname, mb, sc->sc_ncylinders,
sc->sc_nheads, sc->sc_nsectors, sc->sc_secsize, sc->sc_secperunit);
/* Initialize and attach the disk structure */
sc->sc_dk.dk_driver = &cadkdriver;
sc->sc_dk.dk_name = sc->sc_dv.dv_xname;
disk_attach(&sc->sc_dk);
sc->sc_flags |= CAF_ENABLED;
#if !defined(__i386__) && !defined(__vax__)
dk_establish(&sc->sc_dk, &sc->sc_dv); /* XXX */
#endif
#if NRND > 0
/* Attach the device into the rnd source list. */
rnd_attach_source(&sc->sc_rnd_source, sc->sc_dv.dv_xname,
RND_TYPE_DISK, 0);
#endif
}
int
caopen(dev, flags, fmt, p)
dev_t dev;
int flags;
int fmt;
struct proc *p;
{
struct ca_softc *sc;
int unit, part;
unit = CAUNIT(dev);
if (unit >= ca_cd.cd_ndevs)
return (ENXIO);
if ((sc = ca_cd.cd_devs[unit]) == NULL)
return (ENXIO);
if ((sc->sc_flags & CAF_ENABLED) == 0)
return (ENODEV);
part = CAPART(dev);
calock(sc);
if (sc->sc_dk.dk_openmask == 0)
cagetdisklabel(sc);
/* Check that the partition exists. */
if (part != RAW_PART && (part >= sc->sc_dk.dk_label->d_npartitions ||
sc->sc_dk.dk_label->d_partitions[part].p_fstype == FS_UNUSED)) {
caunlock(sc);
return (ENXIO);
}
/* Insure only one open at a time. */
switch (fmt) {
case S_IFCHR:
sc->sc_dk.dk_copenmask |= (1 << part);
break;
case S_IFBLK:
sc->sc_dk.dk_bopenmask |= (1 << part);
break;
}
sc->sc_dk.dk_openmask =
sc->sc_dk.dk_copenmask | sc->sc_dk.dk_bopenmask;
caunlock(sc);
return (0);
}
int
caclose(dev, flags, fmt, p)
dev_t dev;
int flags;
int fmt;
struct proc *p;
{
struct ca_softc *sc;
int part, unit;
unit = CAUNIT(dev);
part = CAPART(dev);
sc = ca_cd.cd_devs[unit];
calock(sc);
switch (fmt) {
case S_IFCHR:
sc->sc_dk.dk_copenmask &= ~(1 << part);
break;
case S_IFBLK:
sc->sc_dk.dk_bopenmask &= ~(1 << part);
break;
}
sc->sc_dk.dk_openmask =
sc->sc_dk.dk_copenmask | sc->sc_dk.dk_bopenmask;
caunlock(sc);
return (0);
}
int
caread(dev, uio, ioflag)
dev_t dev;
struct uio *uio;
int ioflag;
{
return (physio(castrategy, NULL, dev, B_READ, cac_minphys, uio));
}
int
cawrite(dev, uio, ioflag)
dev_t dev;
struct uio *uio;
int ioflag;
{
return (physio(castrategy, NULL, dev, B_WRITE, cac_minphys, uio));
}
int
caioctl(dev, cmd, addr, flag, p)
dev_t dev;
u_long cmd;
caddr_t addr;
int32_t flag;
struct proc *p;
{
struct ca_softc *sc;
int part, unit, error;
unit = CAUNIT(dev);
part = CAPART(dev);
sc = ca_cd.cd_devs[unit];
error = 0;
switch (cmd) {
case DIOCGDINFO:
memcpy(addr, sc->sc_dk.dk_label, sizeof(struct disklabel));
return (0);
case DIOCGPART:
((struct partinfo *)addr)->disklab = sc->sc_dk.dk_label;
((struct partinfo *)addr)->part =
&sc->sc_dk.dk_label->d_partitions[part];
break;
case DIOCWDINFO:
case DIOCSDINFO:
if ((flag & FWRITE) == 0)
return (EBADF);
if ((error = calock(sc)) != 0)
return (error);
sc->sc_flags |= CAF_LABELLING;
error = setdisklabel(sc->sc_dk.dk_label,
(struct disklabel *)addr, /*sc->sc_dk.dk_openmask : */0,
sc->sc_dk.dk_cpulabel);
if (error == 0 && cmd == DIOCWDINFO)
error = writedisklabel(CALABELDEV(dev), castrategy,
sc->sc_dk.dk_label, sc->sc_dk.dk_cpulabel);
sc->sc_flags &= ~CAF_LABELLING;
caunlock(sc);
break;
case DIOCKLABEL:
/* XXX */
break;
case DIOCWLABEL:
if ((flag & FWRITE) == 0)
return (EBADF);
if (*(int *)addr)
sc->sc_flags |= CAF_WLABEL;
else
sc->sc_flags &= ~CAF_WLABEL;
break;
case DIOCGDEFLABEL:
cagetdefaultlabel(sc, (struct disklabel *)addr);
break;
default:
error = ENOTTY;
break;
}
return (error);
}
/*
* Read/write a buffer.
*/
void
castrategy(bp)
struct buf *bp;
{
struct cac_context cc;
struct disklabel *lp;
struct ca_softc *sc;
int part, unit, blkno, flg, cmd;
unit = CAUNIT(bp->b_dev);
part = CAPART(bp->b_dev);
sc = ca_cd.cd_devs[unit];
lp = sc->sc_dk.dk_label;
/*
* The transfer must be a whole number of blocks, offset must not be
* negative.
*/
if ((bp->b_bcount % lp->d_secsize) != 0 || bp->b_blkno < 0) {
bp->b_flags |= B_ERROR;
biodone(bp);
}
/*
* If it's a null transfer, return immediatly.
*/
if (bp->b_bcount == 0) {
bp->b_resid = bp->b_bcount;
biodone(bp);
return;
}
/*
* Do bounds checking, adjust transfer. If error, process.
* If end of partition, just return.
*/
if (part != RAW_PART &&
bounds_check_with_label(bp, lp,
(sc->sc_flags & (CAF_WLABEL | CAF_LABELLING)) != 0) <= 0) {
bp->b_resid = bp->b_bcount;
biodone(bp);
return;
}
/*
* Now convert the block number to absolute and put it in
* terms of the device's logical block size.
*/
if (lp->d_secsize >= DEV_BSIZE)
blkno = bp->b_blkno / (lp->d_secsize / DEV_BSIZE);
else
blkno = bp->b_blkno * (DEV_BSIZE / lp->d_secsize);
if (bp->b_dev != RAW_PART)
blkno += lp->d_partitions[part].p_offset;
bp->b_rawblkno = blkno;
/*
* Which command to issue. Don't let synchronous writes linger in
* the controller's cache.
*/
if ((bp->b_flags & B_READ) != 0) {
cmd = CAC_CMD_READ;
flg = CAC_CCB_DATA_IN;
} else if ((bp->b_flags & B_ASYNC) != 0) {
cmd = CAC_CMD_WRITE;
flg = CAC_CCB_DATA_OUT;
} else {
cmd = CAC_CMD_WRITE_MEDIA;
flg = CAC_CCB_DATA_OUT;
}
cc.cc_context = bp;
cc.cc_handler = cadone;
cc.cc_dv = &sc->sc_dv;
disk_busy(&sc->sc_dk);
cac_cmd(sc->sc_cac, cmd, bp->b_data, bp->b_bcount, sc->sc_unit, blkno,
flg, &cc);
}
/*
* Handle completed transfers.
*/
static void
cadone(ccb, error)
struct cac_ccb *ccb;
int error;
{
struct buf *bp;
struct ca_softc *sc;
sc = (struct ca_softc *)ccb->ccb_context.cc_dv;
bp = (struct buf *)ccb->ccb_context.cc_context;
cac_ccb_free(sc->sc_cac, ccb);
if (error) {
bp->b_flags |= B_ERROR;
bp->b_error = EIO;
bp->b_resid = bp->b_bcount;
} else
bp->b_resid = 0;
disk_unbusy(&sc->sc_dk, 0);
#if NRND > 0
rnd_add_uint32(&sc->sc_rnd_source, bp->b_rawblkno);
#endif
biodone(bp);
}
int
casize(dev)
dev_t dev;
{
struct ca_softc *sc;
int part, unit, omask, size;
unit = CAUNIT(dev);
if (unit >= ca_cd.cd_ndevs)
return (ENXIO);
if ((sc = ca_cd.cd_devs[unit]) == NULL)
return (ENXIO);
if ((sc->sc_flags & CAF_ENABLED) == 0)
return (ENODEV);
part = CAPART(dev);
omask = sc->sc_dk.dk_openmask & (1 << part);
if (omask == 0 && caopen(dev, 0, S_IFBLK, NULL) != 0)
return (-1);
else if (sc->sc_dk.dk_label->d_partitions[part].p_fstype != FS_SWAP)
size = -1;
else
size = sc->sc_dk.dk_label->d_partitions[part].p_size *
(sc->sc_dk.dk_label->d_secsize / DEV_BSIZE);
if (omask == 0 && caclose(dev, 0, S_IFBLK, NULL) != 0)
return (-1);
return (size);
}
/*
* Load the label information from the specified device.
*/
static void
cagetdisklabel(sc)
struct ca_softc *sc;
{
struct disklabel *lp;
char *errstring;
lp = sc->sc_dk.dk_label;
memset(sc->sc_dk.dk_cpulabel, 0, sizeof(struct cpu_disklabel));
cagetdefaultlabel(sc, lp);
if (lp->d_secpercyl == 0) {
lp->d_secpercyl = 100;
/* as long as it's not 0 - readdisklabel divides by it (?) */
}
/* Call the generic disklabel extraction routine. */
errstring = readdisklabel(CAMAKEDEV(0, sc->sc_dv.dv_unit, RAW_PART),
castrategy, lp, sc->sc_dk.dk_cpulabel);
if (errstring != NULL)
printf("%s: %s\n", sc->sc_dv.dv_xname, errstring);
}
/*
* Construct a ficticious label.
*/
static void
cagetdefaultlabel(sc, lp)
struct ca_softc *sc;
struct disklabel *lp;
{
memset(lp, 0, sizeof(struct disklabel));
lp->d_secsize = sc->sc_secsize;
lp->d_ntracks = sc->sc_nheads;
lp->d_nsectors = sc->sc_nsectors;
lp->d_ncylinders = sc->sc_ncylinders;
lp->d_secpercyl = lp->d_ntracks * lp->d_nsectors;
lp->d_type = DTYPE_SCSI; /* XXX */
strcpy(lp->d_typename, "unknown");
strcpy(lp->d_packname, "fictitious");
lp->d_secperunit = sc->sc_secperunit;
lp->d_rpm = 7200;
lp->d_interleave = 1;
lp->d_flags = 0;
lp->d_partitions[RAW_PART].p_offset = 0;
lp->d_partitions[RAW_PART].p_size =
lp->d_secperunit * (lp->d_secsize / DEV_BSIZE);
lp->d_partitions[RAW_PART].p_fstype = FS_UNUSED;
lp->d_npartitions = RAW_PART + 1;
lp->d_magic = DISKMAGIC;
lp->d_magic2 = DISKMAGIC;
lp->d_checksum = dkcksum(lp);
}
/*
* Wait interruptibly for an exclusive lock.
*
* XXX
* Several drivers do this; it should be abstracted and made MP-safe.
*/
static int
calock(sc)
struct ca_softc *sc;
{
int error;
while ((sc->sc_flags & CAF_LOCKED) != 0) {
sc->sc_flags |= CAF_WANTED;
if ((error = tsleep(sc, PRIBIO | PCATCH, "idlck", 0)) != 0)
return (error);
}
sc->sc_flags |= CAF_LOCKED;
return (0);
}
/*
* Unlock and wake up any waiters.
*/
static void
caunlock(sc)
struct ca_softc *sc;
{
sc->sc_flags &= ~CAF_LOCKED;
if ((sc->sc_flags & CAF_WANTED) != 0) {
sc->sc_flags &= ~CAF_WANTED;
wakeup(sc);
}
}
/*
* Take a dump.
*/
int
cadump(dev, blkno, va, size)
dev_t dev;
daddr_t blkno;
caddr_t va;
size_t size;
{
struct ca_softc *sc;
struct disklabel *lp;
int unit, part, nsects, sectoff, totwrt, nwrt;
static int dumping;
/* Check if recursive dump; if so, punt. */
if (dumping)
return (EFAULT);
dumping = 1;
unit = CAUNIT(dev);
if (unit >= ca_cd.cd_ndevs)
return (ENXIO);
if ((sc = ca_cd.cd_devs[unit]) == NULL)
return (ENXIO);
if ((sc->sc_flags & CAF_ENABLED) == 0)
return (ENODEV);
part = CAPART(dev);
/* Convert to disk sectors. Request must be a multiple of size. */
lp = sc->sc_dk.dk_label;
if ((size % lp->d_secsize) != 0)
return (EFAULT);
totwrt = size / lp->d_secsize;
blkno = dbtob(blkno) / lp->d_secsize; /* blkno in DEV_BSIZE units */
nsects = lp->d_partitions[part].p_size;
sectoff = lp->d_partitions[part].p_offset;
/* Check transfer bounds against partition size. */
if ((blkno < 0) || ((blkno + totwrt) > nsects))
return (EINVAL);
/* Offset block number to start of partition. */
blkno += sectoff;
while (totwrt > 0) {
nwrt = max(65536 / lp->d_secsize, totwrt); /* XXX */
if (cac_cmd(sc->sc_cac, CAC_CMD_WRITE_MEDIA, va,
nwrt * lp->d_secsize, sc->sc_unit, blkno,
CAC_CCB_DATA_OUT, NULL))
return (ENXIO);
totwrt -= nwrt;
blkno += nwrt;
va += lp->d_secsize * nwrt;
}
dumping = 0;
return (0);
}

499
sys/dev/ic/cac.c Normal file
View File

@ -0,0 +1,499 @@
/* $NetBSD: cac.c,v 1.1 2000/03/16 14:52:24 ad Exp $ */
/*-
* Copyright (c) 2000 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Andy 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.
*/
/*
* Driver for Compaq array controllers.
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: cac.c,v 1.1 2000/03/16 14:52:24 ad Exp $");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/device.h>
#include <sys/queue.h>
#include <sys/proc.h>
#include <sys/buf.h>
#include <sys/endian.h>
#include <sys/malloc.h>
#include <sys/pool.h>
#include <machine/bswap.h>
#include <machine/bus.h>
#include <dev/ic/cacreg.h>
#include <dev/ic/cacvar.h>
static void cac_ccb_done __P((struct cac_softc *, struct cac_ccb *));
static int cac_print __P((void *, const char *));
static int cac_submatch __P((struct device *, struct cfdata *, void *));
static void cac_ccb_poll __P((struct cac_softc *, struct cac_ccb *, int));
static void cac_shutdown __P((void *));
static SIMPLEQ_HEAD(, cac_softc) cac_hba; /* tailq of HBA softc's */
static void *cac_sdh; /* shutdown hook */
/*
* Initialise our interface to the controller.
*/
int
cac_init(sc, intrstr)
struct cac_softc *sc;
const char *intrstr;
{
struct cac_controller_info cinfo;
struct cac_attach_args caca;
int error, rseg, size, i;
bus_dma_segment_t seg;
struct cac_ccb *ccb;
printf("Compaq %s\n", sc->sc_typestr);
if (intrstr != NULL)
printf("%s: interrupting at %s\n", sc->sc_dv.dv_xname, intrstr);
SIMPLEQ_INIT(&sc->sc_ccb_free);
SIMPLEQ_INIT(&sc->sc_ccb_queue);
size = sizeof(struct cac_ccb) * CAC_MAX_CCBS;
if ((error = bus_dmamem_alloc(sc->sc_dmat, size, NBPG, 0, &seg, 1,
&rseg, BUS_DMA_NOWAIT)) != 0) {
printf("%s: unable to allocate CCBs, error = %d\n",
sc->sc_dv.dv_xname, error);
return (-1);
}
if ((error = bus_dmamem_map(sc->sc_dmat, &seg, rseg, size,
(caddr_t *)&sc->sc_ccbs, BUS_DMA_NOWAIT|BUS_DMA_COHERENT)) != 0) {
printf("%s: unable to map CCBs, error = %d\n",
sc->sc_dv.dv_xname, error);
return (-1);
}
if ((error = bus_dmamap_create(sc->sc_dmat, size, size, 1, 0,
BUS_DMA_NOWAIT, &sc->sc_dmamap)) != 0) {
printf("%s: unable to create CCB DMA map, error = %d\n",
sc->sc_dv.dv_xname, error);
return (-1);
}
if ((error = bus_dmamap_load(sc->sc_dmat, sc->sc_dmamap, sc->sc_ccbs,
size, NULL, BUS_DMA_NOWAIT)) != 0) {
printf("%s: unable to load CCB DMA map, error = %d\n",
sc->sc_dv.dv_xname, error);
return (-1);
}
sc->sc_ccbs_paddr = sc->sc_dmamap->dm_segs[0].ds_addr;
memset(sc->sc_ccbs, 0, size);
ccb = (struct cac_ccb *)sc->sc_ccbs;
for (i = 0; i < CAC_MAX_CCBS; i++, ccb++) {
/* Create the DMA map for this CCB's data */
error = bus_dmamap_create(sc->sc_dmat, CAC_MAX_XFER,
CAC_SG_SIZE, CAC_MAX_XFER, 0,
BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW, &ccb->ccb_dmamap_xfer);
if (error) {
printf("%s: can't create ccb dmamap (%d)\n",
sc->sc_dv.dv_xname, error);
break;
}
ccb->ccb_flags = 0;
ccb->ccb_paddr = sc->sc_ccbs_paddr + i * sizeof(struct cac_ccb);
SIMPLEQ_INSERT_TAIL(&sc->sc_ccb_free, ccb, ccb_chain);
}
if (cac_cmd(sc, CAC_CMD_GET_CTRL_INFO, &cinfo, sizeof(cinfo), 0, 0,
CAC_CCB_DATA_IN, NULL)) {
printf("%s: CAC_CMD_GET_CTRL_INFO failed\n",
sc->sc_dv.dv_xname);
return (-1);
}
for (i = 0; i < cinfo.num_drvs; i++) {
caca.caca_unit = i;
config_found_sm(&sc->sc_dv, &caca, cac_print, cac_submatch);
}
/* Set shutdownhook before we start any device activity. */
if (cac_sdh == NULL) {
SIMPLEQ_INIT(&cac_hba);
cac_sdh = shutdownhook_establish(cac_shutdown, NULL);
}
sc->sc_cl->cl_intr_enable(sc, CAC_INT_ENABLE);
SIMPLEQ_INSERT_HEAD(&cac_hba, sc, sc_chain);
return (0);
}
/*
* Shutdown the controller.
*/
static void
cac_shutdown(cookie)
void *cookie;
{
struct cac_softc *sc;
char buf[512];
printf("shutting down cac devices...");
for (sc = SIMPLEQ_FIRST(&cac_hba); sc != NULL;
sc = SIMPLEQ_NEXT(sc, sc_chain)) {
/* XXX documentation on this is a bit fuzzy. */
memset(buf, 0, sizeof (buf));
buf[0] = 1;
cac_cmd(sc, CAC_CMD_FLUSH_CACHE, buf, sizeof(buf), 0, 0,
CAC_CCB_DATA_OUT, NULL);
}
DELAY(5000*1000);
printf(" done\n");
}
/*
* Print attach message for a subdevice.
*/
static int
cac_print(aux, pnp)
void *aux;
const char *pnp;
{
struct cac_attach_args *caca;
caca = (struct cac_attach_args *)aux;
if (pnp)
printf("block device at %s", pnp);
printf(" unit %d", caca->caca_unit);
return (UNCONF);
}
/*
* Match a subdevice.
*/
static int
cac_submatch(parent, cf, aux)
struct device *parent;
struct cfdata *cf;
void *aux;
{
struct cac_attach_args *caca;
caca = (struct cac_attach_args *)aux;
if (cf->cacacf_unit != CACACF_UNIT_UNKNOWN &&
cf->cacacf_unit != caca->caca_unit)
return (0);
return (cf->cf_attach->ca_match(parent, cf, aux));
}
/*
* Handle an interrupt from the controller: process finished CCBs and
* dequeue any waiting CCBs.
*/
int
cac_intr(xxx_sc)
void *xxx_sc;
{
struct cac_softc *sc;
struct cac_ccb *ccb;
paddr_t completed;
int off;
sc = (struct cac_softc *)xxx_sc;
if (!sc->sc_cl->cl_intr_pending(sc))
return (0);
while ((completed = sc->sc_cl->cl_completed(sc)) != 0) {
off = (completed & ~3) - sc->sc_ccbs_paddr;
ccb = (struct cac_ccb *)(sc->sc_ccbs + off);
bus_dmamap_sync(sc->sc_dmat, sc->sc_dmamap, off,
sizeof(struct cac_ccb),
BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD);
cac_ccb_done(sc, ccb);
}
cac_ccb_start(sc, NULL);
return (1);
}
/*
* Execute a [polled] command.
*/
int
cac_cmd(sc, command, data, datasize, drive, blkno, flags, context)
struct cac_softc *sc;
int command;
void *data;
int datasize;
int drive;
int blkno;
int flags;
struct cac_context *context;
{
struct cac_ccb *ccb;
struct cac_sgb *sgb;
int s, i, rv;
if ((ccb = cac_ccb_alloc(sc, 0)) == NULL) {
printf("%s: unable to alloc CCB", sc->sc_dv.dv_xname);
return (1);
}
if ((flags & (CAC_CCB_DATA_IN | CAC_CCB_DATA_OUT)) != 0) {
bus_dmamap_load(sc->sc_dmat, ccb->ccb_dmamap_xfer, (void *)data,
datasize, NULL, BUS_DMA_NOWAIT);
bus_dmamap_sync(sc->sc_dmat, ccb->ccb_dmamap_xfer, 0, datasize,
(flags & CAC_CCB_DATA_IN) != 0 ? BUS_DMASYNC_PREREAD :
BUS_DMASYNC_PREWRITE);
sgb = ccb->ccb_seg;
for (i = 0; i < ccb->ccb_dmamap_xfer->dm_nsegs; i++, sgb++) {
sgb->length =
htole32(ccb->ccb_dmamap_xfer->dm_segs[i].ds_len);
sgb->addr =
htole32(ccb->ccb_dmamap_xfer->dm_segs[i].ds_addr);
}
}
ccb->ccb_hdr.drive = drive;
ccb->ccb_hdr.size = htole16((sizeof(struct cac_req) +
sizeof(struct cac_sgb) * CAC_SG_SIZE) >> 2);
ccb->ccb_req.bcount = htole16(howmany(datasize, DEV_BSIZE));
ccb->ccb_req.command = command;
ccb->ccb_req.sgcount = i;
ccb->ccb_req.blkno = htole32(blkno);
ccb->ccb_flags = flags;
ccb->ccb_datasize = datasize;
if (context == NULL) {
memset(&ccb->ccb_context, 0, sizeof(struct cac_context));
s = splbio();
if (cac_ccb_start(sc, ccb)) {
cac_ccb_free(sc, ccb);
rv = -1;
} else {
cac_ccb_poll(sc, ccb, 2000);
cac_ccb_free(sc, ccb);
rv = 0;
}
splx(s);
} else {
memcpy(&ccb->ccb_context, context, sizeof(struct cac_context));
rv = cac_ccb_start(sc, ccb);
}
return (rv);
}
/*
* Wait for the specified CCB to complete. Must be called at splbio.
*/
static void
cac_ccb_poll(sc, ccb, timo)
struct cac_softc *sc;
struct cac_ccb *ccb;
int timo;
{
struct cac_ccb *ccb_done = NULL;
paddr_t completed;
int off;
for (;;) {
for (; timo != 0; timo--) {
if ((completed = sc->sc_cl->cl_completed(sc)) != 0)
break;
DELAY(100);
}
if (timo == 0)
panic("%s: cac_ccb_poll: timeout", sc->sc_dv.dv_xname);
off = (completed & ~3) - sc->sc_ccbs_paddr;
ccb_done = (struct cac_ccb *)(sc->sc_ccbs + off);
bus_dmamap_sync(sc->sc_dmat, sc->sc_dmamap, off,
sizeof(struct cac_ccb),
BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD);
cac_ccb_done(sc, ccb_done);
if (ccb_done == ccb)
break;
}
}
/*
* Enqueue the specifed command (if any) and attempt to start all enqueued
* commands. Must be called at splbio.
*/
int
cac_ccb_start(sc, ccb)
struct cac_softc *sc;
struct cac_ccb *ccb;
{
int s;
s = splbio();
if (ccb != NULL)
SIMPLEQ_INSERT_TAIL(&sc->sc_ccb_queue, ccb, ccb_chain);
while ((ccb = SIMPLEQ_FIRST(&sc->sc_ccb_queue)) != NULL) {
if (sc->sc_cl->cl_fifo_full(sc)) {
splx(s);
return (-1);
}
SIMPLEQ_REMOVE_HEAD(&sc->sc_ccb_queue, ccb, ccb_chain);
bus_dmamap_sync(sc->sc_dmat, sc->sc_dmamap,
(caddr_t)ccb - sc->sc_ccbs, sizeof(struct cac_ccb),
BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
sc->sc_cl->cl_submit(sc, ccb->ccb_paddr);
}
splx(s);
return (0);
}
/*
* Process a finished CCB.
*/
static void
cac_ccb_done(sc, ccb)
struct cac_softc *sc;
struct cac_ccb *ccb;
{
int error;
error = 0;
if ((ccb->ccb_flags & (CAC_CCB_DATA_IN | CAC_CCB_DATA_OUT)) != 0) {
bus_dmamap_sync(sc->sc_dmat, ccb->ccb_dmamap_xfer, 0,
ccb->ccb_datasize, ccb->ccb_flags & CAC_CCB_DATA_IN ?
BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE);
bus_dmamap_unload(sc->sc_dmat, ccb->ccb_dmamap_xfer);
}
if ((ccb->ccb_req.error & CAC_RET_SOFT_ERROR) != 0)
printf("%s: soft error\n", sc->sc_dv.dv_xname);
if ((ccb->ccb_req.error & CAC_RET_HARD_ERROR) != 0) {
error = 1;
printf("%s: hard error\n", sc->sc_dv.dv_xname);
}
if ((ccb->ccb_req.error & CAC_RET_CMD_REJECTED) != 0) {
error = 1;
printf("%s: invalid request\n", sc->sc_dv.dv_xname);
}
if (ccb->ccb_context.cc_handler != NULL)
ccb->ccb_context.cc_handler(ccb, error);
}
/*
* Get a free CCB.
*/
struct cac_ccb *
cac_ccb_alloc(sc, nosleep)
struct cac_softc *sc;
int nosleep;
{
struct cac_ccb *ccb;
int s;
s = splbio();
for (;;) {
if ((ccb = SIMPLEQ_FIRST(&sc->sc_ccb_free)) != NULL) {
SIMPLEQ_REMOVE_HEAD(&sc->sc_ccb_free, ccb, ccb_chain);
break;
}
if (nosleep) {
ccb = NULL;
break;
}
tsleep(&sc->sc_ccb_free, PRIBIO, "cacccb", 0);
}
splx(s);
if (ccb != NULL)
memset(ccb, 0, 276); /* XXX */
return (ccb);
}
/*
* Put a CCB onto the freelist.
*/
void
cac_ccb_free(sc, ccb)
struct cac_softc *sc;
struct cac_ccb *ccb;
{
int s;
s = splbio();
ccb->ccb_flags = 0;
SIMPLEQ_INSERT_HEAD(&sc->sc_ccb_free, ccb, ccb_chain);
/* Wake anybody waiting for a free ccb */
if (SIMPLEQ_NEXT(ccb, ccb_chain) == NULL)
wakeup(&sc->sc_ccb_free);
splx(s);
}
/*
* Adjust the size of a transfer.
*/
void
cac_minphys(bp)
struct buf *bp;
{
if (bp->b_bcount > CAC_MAX_XFER / DEV_BSIZE) /* XXX */
bp->b_bcount = CAC_MAX_XFER / DEV_BSIZE; /* XXX */
}

171
sys/dev/ic/cacreg.h Normal file
View File

@ -0,0 +1,171 @@
/* $NetBSD: cacreg.h,v 1.1 2000/03/16 14:52:24 ad Exp $ */
/*-
* Copyright (c) 2000 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Andy 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 Jonathan Lemon
* 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.
*
* FreeBSD: src/sys/dev/ida/idareg.h,v 1.2 1999/08/28 00:41:55 peter Exp
*/
#ifndef _IC_CACREG_H_
#define _IC_CACREG_H_
#define cac_inl(sc, port) \
bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, port)
#define cac_outl(sc, port, val) \
bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, port, val)
/* Board register offsets */
#define CAC_REG_CMD_FIFO 0x04
#define CAC_REG_DONE_FIFO 0x08
#define CAC_REG_INT_MASK 0x0C
#define CAC_REG_STATUS 0x10
#define CAC_REG_INT_PENDING 0x14
#define CAC_42REG_CMD_FIFO 0x40
#define CAC_42REG_DONE_FIFO 0x44
#define CAC_42REG_INT_MASK 0x34
#define CAC_42REG_STATUS 0x30
#define CAC_42REG_INT_PENDING 0x08
/* Interrupt mask values */
#define CAC_INT_DISABLE 0x00
#define CAC_INT_ENABLE 0x01
/* Command types */
#define CAC_CMD_GET_LOG_DRV_INFO 0x10
#define CAC_CMD_GET_CTRL_INFO 0x11
#define CAC_CMD_SENSE_DRV_STATUS 0x12
#define CAC_CMD_START_RECOVERY 0x13
#define CAC_CMD_GET_PHYS_DRV_INFO 0x15
#define CAC_CMD_BLINK_DRV_LEDS 0x16
#define CAC_CMD_SENSE_DRV_LEDS 0x17
#define CAC_CMD_GET_LOG_DRV_EXT 0x18
#define CAC_CMD_GET_CTRL_INFO 0x11
#define CAC_CMD_READ 0x20
#define CAC_CMD_WRITE 0x30
#define CAC_CMD_WRITE_MEDIA 0x31
#define CAC_CMD_GET_CONFIG 0x50
#define CAC_CMD_SET_CONFIG 0x51
#define CAC_CMD_FLUSH_CACHE 0xc2
/* Return status codes */
#define CAC_RET_SOFT_ERROR 0x02
#define CAC_RET_HARD_ERROR 0x04
#define CAC_RET_CMD_REJECTED 0x14
struct cac_drive_info {
u_int16_t secsize __attribute__ ((packed));
u_int32_t secperunit __attribute__ ((packed));
u_int16_t ncylinders __attribute__ ((packed));
u_int8_t nheads __attribute__ ((packed));
u_int8_t signature __attribute__ ((packed));
u_int8_t psectors __attribute__ ((packed));
u_int16_t wprecomp __attribute__ ((packed));
u_int8_t max_acc __attribute__ ((packed));
u_int8_t control __attribute__ ((packed));
u_int16_t pcylinders __attribute__ ((packed));
u_int8_t ptracks __attribute__ ((packed));
u_int16_t landing_zone __attribute__ ((packed));
u_int8_t nsectors __attribute__ ((packed));
u_int8_t checksum __attribute__ ((packed));
u_int8_t mirror __attribute__ ((packed));
};
struct cac_controller_info {
u_int8_t num_drvs __attribute__ ((packed));
u_int32_t signature __attribute__ ((packed));
u_int8_t firm_rev[4] __attribute__ ((packed));
};
struct cac_hdr {
u_int8_t drive; /* logical drive */
u_int8_t priority; /* block priority */
u_int16_t size; /* size of request, in words */
};
struct cac_req {
u_int16_t next; /* offset of next request */
u_int8_t command; /* command */
u_int8_t error; /* return error code */
u_int32_t blkno; /* block number */
u_int16_t bcount; /* block count */
u_int8_t sgcount; /* number of scatter/gather entries */
u_int8_t reserved; /* reserved */
};
struct cac_sgb {
u_int32_t length; /* length of S/G segment */
u_int32_t addr; /* physical address of block */
};
/*
* Stupid macros to deal with alignment/endianness issues.
*/
#define CAC_GET1(x) \
(((u_char *)&(x))[0])
#define CAC_GET2(x) \
(((u_char *)&(x))[0] | (((u_char *)&(x))[1] << 8))
#define CAC_GET4(x) \
((((u_char *)&(x))[0] | (((u_char *)&(x))[1] << 8)) | \
(((u_char *)&(x))[0] << 16 | (((u_char *)&(x))[1] << 24)))
#endif /* !_IC_CACREG_H_ */

119
sys/dev/ic/cacvar.h Normal file
View File

@ -0,0 +1,119 @@
/* $NetBSD: cacvar.h,v 1.1 2000/03/16 14:52:24 ad Exp $ */
/*-
* Copyright (c) 2000 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Andy 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.
*/
#ifndef _IC_CACVAR_H_
#define _IC_CACVAR_H_
#include "locators.h"
#define CAC_MAX_CCBS 64
#define CAC_MAX_XFER 1048576
#define CAC_SG_SIZE 32
struct cac_softc;
struct cac_ccb;
struct cac_context {
void (*cc_handler) __P((struct cac_ccb *, int));
struct device *cc_dv;
void *cc_context;
};
struct cac_ccb {
/* Data the controller will touch - 276 bytes */
struct cac_hdr ccb_hdr;
struct cac_req ccb_req;
struct cac_sgb ccb_seg[CAC_SG_SIZE];
u_int32_t ccb_junk_context;
/* Data the controller won't touch */
int ccb_flags;
int ccb_datasize;
paddr_t ccb_paddr;
bus_dmamap_t ccb_dmamap_xfer;
SIMPLEQ_ENTRY(cac_ccb) ccb_chain;
struct cac_context ccb_context;
};
#define CAC_CCB_DATA_IN 0x0001
#define CAC_CCB_DATA_OUT 0x0002
struct cac_linkage {
void (*cl_submit) __P((struct cac_softc *, paddr_t));
paddr_t (*cl_completed) __P((struct cac_softc *));
int (*cl_intr_pending) __P((struct cac_softc *));
void (*cl_intr_enable) __P((struct cac_softc *, int));
int (*cl_fifo_full) __P((struct cac_softc *));
};
struct cac_softc {
struct device sc_dv;
bus_space_tag_t sc_iot;
bus_space_handle_t sc_ioh;
bus_dma_tag_t sc_dmat;
bus_dmamap_t sc_dmamap;
void *sc_ih;
struct cac_linkage *sc_cl;
char *sc_typestr;
caddr_t sc_ccbs;
paddr_t sc_ccbs_paddr;
SIMPLEQ_HEAD(, cac_ccb) sc_ccb_free;
SIMPLEQ_HEAD(, cac_ccb) sc_ccb_queue;
SIMPLEQ_ENTRY(cac_softc) sc_chain;
};
struct cac_attach_args {
int caca_unit;
};
#define CACACF_UNIT 0
#define cacacf_unit cf_loc[CACACF_UNIT]
#define CACACF_UNIT_UNKNOWN -1
int cac_cmd __P((struct cac_softc *, int, void *, int, int, int, int,
struct cac_context *));
void cac_minphys __P((struct buf *));
int cac_intr __P((void *));
int cac_init __P((struct cac_softc *, const char *));
void cac_ccb_free __P((struct cac_softc *, struct cac_ccb *));
int cac_ccb_start __P((struct cac_softc *, struct cac_ccb *));
struct cac_ccb *cac_ccb_alloc __P((struct cac_softc *, int));
#endif /* !_IC_CACVAR_H_ */

290
sys/dev/pci/cac_pci.c Normal file
View File

@ -0,0 +1,290 @@
/* $NetBSD: cac_pci.c,v 1.1 2000/03/16 14:52:23 ad Exp $ */
/*-
* Copyright (c) 2000 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Andy 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.
*/
/*
* PCI front-end for cac(4) driver.
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: cac_pci.c,v 1.1 2000/03/16 14:52:23 ad Exp $");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/device.h>
#include <sys/queue.h>
#include <sys/proc.h>
#include <sys/buf.h>
#include <machine/endian.h>
#include <machine/bus.h>
#include <dev/pci/pcidevs.h>
#include <dev/pci/pcivar.h>
#include <dev/ic/cacreg.h>
#include <dev/ic/cacvar.h>
#define PCI_CBIO 0x10 /* Configuration base I/O address */
#define PCI_CBMA 0x14 /* Configuration base memory address */
static int cac_pci_match __P((struct device *, struct cfdata *, void *));
static void cac_pci_attach __P((struct device *, struct device *, void *));
static void cac_pci_l0_submit __P((struct cac_softc *, paddr_t));
static paddr_t cac_pci_l0_completed __P((struct cac_softc *));
static int cac_pci_l0_intr_pending __P((struct cac_softc *));
static void cac_pci_l0_intr_enable __P((struct cac_softc *, int));
static int cac_pci_l0_fifo_full __P((struct cac_softc *));
static void cac_pci_l1_submit __P((struct cac_softc *, paddr_t));
static paddr_t cac_pci_l1_completed __P((struct cac_softc *));
static int cac_pci_l1_intr_pending __P((struct cac_softc *));
static void cac_pci_l1_intr_enable __P((struct cac_softc *, int));
static int cac_pci_l1_fifo_full __P((struct cac_softc *));
struct cfattach cac_pci_ca = {
sizeof(struct cac_softc), cac_pci_match, cac_pci_attach
};
static struct cac_linkage cac_pci_l0 = {
cac_pci_l0_submit,
cac_pci_l0_completed,
cac_pci_l0_intr_pending,
cac_pci_l0_intr_enable,
cac_pci_l0_fifo_full
};
static struct cac_linkage cac_pci_l1 = {
cac_pci_l1_submit,
cac_pci_l1_completed,
cac_pci_l1_intr_pending,
cac_pci_l1_intr_enable,
cac_pci_l1_fifo_full
};
/* This block of code inspired by Compaq's Linux driver. */
struct cac_type {
int ct_id; /* PCI ID */
struct cac_linkage *ct_linkage; /* Command interface */
char *ct_typestr; /* Textual description */
} static cac_type[] = {
#ifdef notdef
{ 0x0040110e, &cac_pci_lX, "IDA" },
{ 0x0140110e, &cac_pci_lX, "IDA 2" },
{ 0x1040110e, &cac_pci_lX, "IAES" },
{ 0x2040110e, &cac_pci_lX, "SMART" },
#endif
{ 0x3040110E, &cac_pci_l0, "SMART-2/E" },
{ 0xae100e11, &cac_pci_l0, "SMART-2/P" }, /* XXX also SA 211? */
{ 0x40300e11, &cac_pci_l0, "SMART-2/P" },
{ 0x40310e11, &cac_pci_l0, "SMART-2SL" },
{ 0x40320e11, &cac_pci_l0, "Smart Array 3200" },
{ 0x40330e11, &cac_pci_l0, "Smart Array 3100ES" },
{ 0x40340e11, &cac_pci_l0, "Smart Array 221" },
{ 0x40400e11, &cac_pci_l1, "Integrated Array" },
{ 0x40500e11, &cac_pci_l1, "Smart Array 4200" },
{ 0x40510e11, &cac_pci_l1, "Smart Array 4200ES" },
{ 0xffffffff, NULL, NULL },
};
static int
cac_pci_match(parent, match, aux)
struct device *parent;
struct cfdata *match;
void *aux;
{
struct pci_attach_args *pa;
int i;
pa = (struct pci_attach_args *)aux;
for (i = 0; cac_type[i].ct_linkage != NULL; i++)
if (pa->pa_id == cac_type[i].ct_id)
return (1);
return (0);
}
static void
cac_pci_attach(parent, self, aux)
struct device *parent;
struct device *self;
void *aux;
{
struct pci_attach_args *pa;
struct cac_softc *sc;
pci_chipset_tag_t pc;
pci_intr_handle_t ih;
const char *intrstr;
pcireg_t csr;
int i;
sc = (struct cac_softc *)self;
pa = (struct pci_attach_args *)aux;
pc = pa->pa_pc;
printf(": ");
if (pci_mapreg_map(pa, PCI_CBIO, PCI_MAPREG_TYPE_IO, 0, &sc->sc_iot,
&sc->sc_ioh, NULL, NULL)) {
printf("can't map i/o space\n");
return;
}
sc->sc_dmat = pa->pa_dmat;
/* Enable the device. */
csr = 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,
csr | PCI_COMMAND_MASTER_ENABLE);
/* Map and establish the interrupt. */
if (pci_intr_map(pc, pa->pa_intrtag, pa->pa_intrpin,
pa->pa_intrline, &ih)) {
printf("can't map interrupt\n");
return;
}
intrstr = pci_intr_string(pc, ih);
sc->sc_ih = pci_intr_establish(pc, ih, IPL_BIO, cac_intr, sc);
if (sc->sc_ih == NULL) {
printf("can't establish interrupt");
if (intrstr != NULL)
printf(" at %s", intrstr);
printf("\n");
return;
}
/* Now attach to the bus-independent code */
for (i = 0; cac_type[i].ct_linkage != NULL; i++)
if (pa->pa_id == cac_type[i].ct_id)
break;
sc->sc_typestr = cac_type[i].ct_typestr;
sc->sc_cl = cac_type[i].ct_linkage;
cac_init(sc, intrstr);
}
static void
cac_pci_l0_submit(sc, addr)
struct cac_softc *sc;
paddr_t addr;
{
cac_outl(sc, CAC_REG_CMD_FIFO, addr);
}
static paddr_t
cac_pci_l0_completed(sc)
struct cac_softc *sc;
{
return (cac_inl(sc, CAC_REG_DONE_FIFO));
}
static int
cac_pci_l0_intr_pending(sc)
struct cac_softc *sc;
{
return (cac_inl(sc, CAC_REG_INT_PENDING));
}
static void
cac_pci_l0_intr_enable(sc, state)
struct cac_softc *sc;
int state;
{
cac_outl(sc, CAC_REG_INT_MASK, state);
}
static int
cac_pci_l0_fifo_full(sc)
struct cac_softc *sc;
{
return (cac_inl(sc, CAC_REG_CMD_FIFO) == 0);
}
static void
cac_pci_l1_submit(sc, addr)
struct cac_softc *sc;
paddr_t addr;
{
cac_outl(sc, CAC_42REG_CMD_FIFO, addr);
}
static paddr_t
cac_pci_l1_completed(sc)
struct cac_softc *sc;
{
int32_t val;
if ((val = cac_inl(sc, CAC_42REG_DONE_FIFO)) == -1)
return (0);
cac_outl(sc, CAC_42REG_DONE_FIFO, 0);
return ((paddr_t)val);
}
static int
cac_pci_l1_intr_pending(sc)
struct cac_softc *sc;
{
return (cac_inl(sc, CAC_42REG_INT_PENDING) &
cac_inl(sc, CAC_42REG_STATUS));
}
static void
cac_pci_l1_intr_enable(sc, state)
struct cac_softc *sc;
int state;
{
cac_outl(sc, CAC_REG_INT_MASK, (state ? 0 : 8)); /* XXX */
}
static int
cac_pci_l1_fifo_full(sc)
struct cac_softc *sc;
{
return (~cac_inl(sc, CAC_42REG_CMD_FIFO));
}

View File

@ -1,4 +1,4 @@
# $NetBSD: files.pci,v 1.77 2000/03/04 10:27:59 elric Exp $
# $NetBSD: files.pci,v 1.78 2000/03/16 14:52:23 ad Exp $
#
# Config file and device description for machine-independent PCI code.
# Included by ports that need it. Requires that the SCSI files be
@ -18,6 +18,10 @@ file dev/pci/pci_subr.c pci
attach ahc at pci with ahc_pci: ahc_seeprom, smc93cx6
file dev/pci/ahc_pci.c ahc_pci
# Compaq RAID controllers
attach cac at pci with cac_pci
file dev/pci/cac_pci.c cac_pci
# DPT EATA SCSI controllers
attach dpt at pci with dpt_pci
file dev/pci/dpt_pci.c dpt_pci

View File

@ -1,4 +1,4 @@
/* $NetBSD: conf.h,v 1.72 2000/02/22 21:11:28 tls Exp $ */
/* $NetBSD: conf.h,v 1.73 2000/03/16 14:52:22 ad Exp $ */
/*-
* Copyright (c) 1990, 1993
@ -469,6 +469,12 @@ cdev_decl(ss);
bdev_decl(uk);
cdev_decl(uk);
/*
* [bc]dev_decl()s for Compaq RAID devices.
*/
bdev_decl(ca);
cdev_decl(ca);
/*
* [bc]dev_decl()s for 'fake' network devices.
*/