NetBSD/sys/arch/amiga/dev/idesc.c

1148 lines
29 KiB
C

/* $NetBSD: idesc.c,v 1.23 1996/05/30 03:15:07 mhitch Exp $ */
/*
* Copyright (c) 1994 Michael L. Hitch
* Copyright (c) 1993, 1994 Charles Hannum.
* Copyright (c) 1990 The Regents of the University of California.
* All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* Don Ahn.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
*
* @(#)wd.c 7.4 (Berkeley) 5/25/91
*/
/*
* Copyright (c) 1994 Michael L. Hitch
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Brad Pepers
* 4. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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.
*/
/*
* A4000 IDE interface, emulating a SCSI controller
*/
#include "idesc.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/buf.h>
#include <sys/dkstat.h>
#include <sys/disklabel.h>
#include <sys/dkstat.h>
#include <sys/malloc.h>
#include <sys/proc.h>
#include <sys/reboot.h>
#include <sys/file.h>
#include <sys/ioctl.h>
#include <scsi/scsi_all.h>
#include <scsi/scsi_disk.h>
#include <scsi/scsiconf.h>
#include <amiga/amiga/device.h>
#include <amiga/amiga/cia.h>
#include <amiga/amiga/custom.h>
#include <amiga/amiga/isr.h>
#include <amiga/dev/zbusvar.h>
#define b_cylin b_resid
/* defines */
struct regs {
volatile u_short ide_data; /* 00 */
char ____pad0[4];
volatile u_char ide_error; /* 06 */
#define ide_precomp ide_error
char ____pad1[3];
volatile u_char ide_seccnt; /* 0a */
char ____pad2[3];
volatile u_char ide_sector; /* 0e */
char ____pad3[3];
volatile u_char ide_cyl_lo; /* 12 */
char ____pad4[3];
volatile u_char ide_cyl_hi; /* 16 */
char ____pad5[3];
volatile u_char ide_sdh; /* 1a */
char ____pad6[3];
volatile u_char ide_command; /* 1e */
#define ide_status ide_command
char ____pad7;
char ____pad8[0xfe0];
volatile short ide_intpnd; /* 1000 */
char ____pad9[24];
volatile u_char ide_altsts; /* 101a */
#define ide_ctlr ide_altsts
};
typedef volatile struct regs *ide_regmap_p;
#define IDES_BUSY 0x80 /* controller busy bit */
#define IDES_READY 0x40 /* selected drive is ready */
#define IDES_WRTFLT 0x20 /* Write fault */
#define IDES_SEEKCMPLT 0x10 /* Seek complete */
#define IDES_DRQ 0x08 /* Data request bit */
#define IDES_ECCCOR 0x04 /* ECC correction made in data */
#define IDES_INDEX 0x02 /* Index pulse from selected drive */
#define IDES_ERR 0x01 /* Error detect bit */
#define IDEC_RESTORE 0x10
#define IDEC_READ 0x20
#define IDEC_WRITE 0x30
#define IDEC_XXX 0x40
#define IDEC_FORMAT 0x50
#define IDEC_XXXX 0x70
#define IDEC_DIAGNOSE 0x90
#define IDEC_IDC 0x91
#define IDEC_READP 0xec
#define IDECTL_IDS 0x02 /* Interrupt disable */
struct ideparams {
/* drive info */
short idep_config; /* general configuration */
short idep_fixedcyl; /* number of non-removable cylinders */
short idep_removcyl; /* number of removable cylinders */
short idep_heads; /* number of heads */
short idep_unfbytespertrk; /* number of unformatted bytes/track */
short idep_unfbytes; /* number of unformatted bytes/sector */
short idep_sectors; /* number of sectors */
short idep_minisg; /* minimum bytes in inter-sector gap*/
short idep_minplo; /* minimum bytes in postamble */
short idep_vendstat; /* number of words of vendor status */
/* controller info */
char idep_cnsn[20]; /* controller serial number */
short idep_cntype; /* controller type */
#define IDETYPE_SINGLEPORTSECTOR 1 /* single port, single sector buffer */
#define IDETYPE_DUALPORTMULTI 2 /* dual port, multiple sector buffer */
#define IDETYPE_DUALPORTMULTICACHE 3 /* above plus track cache */
short idep_cnsbsz; /* sector buffer size, in sectors */
short idep_necc; /* ecc bytes appended */
char idep_rev[8]; /* firmware revision */
char idep_model[40]; /* model name */
short idep_nsecperint; /* sectors per interrupt */
short idep_usedmovsd; /* can use double word read/write? */
};
/*
* Per drive structure.
* N per controller (presently 2) (DRVS_PER_CTLR)
*/
struct ide_softc {
struct device sc_dev;
long sc_bcount; /* byte count left */
long sc_mbcount; /* total byte count left */
short sc_skip; /* blocks already transferred */
short sc_mskip; /* blocks already transfereed for multi */
long sc_blknum; /* starting block of active request */
u_char *sc_buf; /* buffer address of active request */
long sc_blkcnt; /* block count of active request */
int sc_flags;
#define IDEF_ALIVE 0x01 /* it's a valid device */
short sc_error;
char sc_drive;
char sc_state;
long sc_secpercyl;
long sc_sectors;
struct buf sc_dq;
struct ideparams sc_params;
};
struct ide_pending {
TAILQ_ENTRY(ide_pending) link;
struct scsi_xfer *xs;
};
/*
* Per controller structure.
*/
struct idec_softc
{
struct device sc_dev;
struct isr sc_isr;
struct scsi_link sc_link; /* proto for sub devices */
ide_regmap_p sc_cregs; /* driver specific regs */
volatile u_char *sc_a1200; /* A1200 interrupt control */
TAILQ_HEAD(,ide_pending) sc_xslist; /* LIFO */
struct ide_pending sc_xsstore[8][8]; /* one for every unit */
struct scsi_xfer *sc_xs; /* transfer from high level code */
int sc_flags;
#define IDECF_ALIVE 0x01 /* Controller is alive */
#define IDECF_ACTIVE 0x02
#define IDECF_SINGLE 0x04 /* sector at a time mode */
#define IDECF_READ 0x08 /* Current operation is read */
#define IDECF_A1200 0x10 /* A1200 IDE */
struct ide_softc *sc_cur; /* drive we are currently doing work for */
int state;
int saved;
int retry;
char sc_stat[2];
struct ide_softc sc_ide[2];
};
int ide_scsicmd __P((struct scsi_xfer *));
int idescprint __P((void *auxp, char *));
void idescattach __P((struct device *, struct device *, void *));
int idescmatch __P((struct device *, void *, void *));
int ideicmd __P((struct idec_softc *, int, void *, int, void *, int));
int idego __P((struct idec_softc *, struct scsi_xfer *));
int idegetsense __P((struct idec_softc *, struct scsi_xfer *));
void ideabort __P((struct idec_softc *, ide_regmap_p, char *));
void ideerror __P((struct idec_softc *, ide_regmap_p, u_char));
int idestart __P((struct idec_softc *));
int idereset __P((struct idec_softc *));
void idesetdelay __P((int));
void ide_scsidone __P((struct idec_softc *, int));
void ide_donextcmd __P((struct idec_softc *));
int idesc_intr __P((void *));
struct scsi_adapter idesc_scsiswitch = {
ide_scsicmd,
minphys, /* no max transfer len, at this level */
0, /* no lun support */
0, /* no lun support */
};
struct scsi_device idesc_scsidev = {
NULL, /* use default error handler */
NULL, /* do not have a start functio */
NULL, /* have no async handler */
NULL, /* Use default done routine */
};
struct cfattach idesc_ca = {
sizeof(struct idec_softc), idescmatch, idescattach
};
struct cfdriver idesc_cd = {
NULL, "idesc", DV_DULL, NULL, 0
};
struct {
short ide_err;
char scsi_sense_key;
char scsi_sense_qual;
} sense_convert[] = {
{ 0x0001, 0x03, 0x13}, /* Data address mark not found */
{ 0x0002, 0x04, 0x06}, /* Reference position not found */
{ 0x0004, 0x05, 0x20}, /* Invalid command */
{ 0x0010, 0x03, 0x12}, /* ID address mark not found */
{ 0x0040, 0x03, 0x11}, /* Unrecovered read error */
{ 0x0080, 0x03, 0x11}, /* Bad block mark detected */
{ 0x0000, 0x05, 0x00} /* unknown */
};
/*
* protos.
*/
int idecommand __P((struct ide_softc *, int, int, int, int, int));
int idewait __P((struct idec_softc *, int));
int idegetctlr __P((struct ide_softc *));
int ideiread __P((struct ide_softc *, long, u_char *, int));
int ideiwrite __P((struct ide_softc *, long, u_char *, int));
#define wait_for_drq(ide) idewait(ide, IDES_DRQ)
#define wait_for_ready(ide) idewait(ide, IDES_READY | IDES_SEEKCMPLT)
#define wait_for_unbusy(ide) idewait(ide,0)
int ide_no_int = 0;
#ifdef DEBUG
void ide_dump_regs __P((ide_regmap_p));
int ide_debug = 0;
#define TRACE0(arg) if (ide_debug > 1) printf(arg)
#define TRACE1(arg1,arg2) if (ide_debug > 1) printf(arg1,arg2)
#define QPRINTF(a) if (ide_debug > 1) printf a
#else /* !DEBUG */
#define TRACE0(arg)
#define TRACE1(arg1,arg2)
#define QPRINTF(a)
#endif /* !DEBUG */
/*
* if we are an A4000 we are here.
*/
int
idescmatch(pdp, match, auxp)
struct device *pdp;
void *match, *auxp;
{
char *mbusstr;
mbusstr = auxp;
if ((is_a4000() || is_a1200()) && matchname(auxp, "idesc"))
return(1);
return(0);
}
void
idescattach(pdp, dp, auxp)
struct device *pdp, *dp;
void *auxp;
{
ide_regmap_p rp;
struct idec_softc *sc;
int i;
sc = (struct idec_softc *)dp;
if (is_a4000())
sc->sc_cregs = rp = (ide_regmap_p) ztwomap(0xdd2020);
else {
/* Let's hope the A1200 will work with the same regs */
sc->sc_cregs = rp = (ide_regmap_p) ztwomap(0xda0000);
sc->sc_a1200 = ztwomap(0xda8000 + 0x1000);
sc->sc_flags |= IDECF_A1200;
printf(" A1200 @ %p:%p", rp, sc->sc_a1200);
}
#ifdef DEBUG
if (ide_debug)
ide_dump_regs(rp);
#endif
rp->ide_error = 0x5a;
rp->ide_cyl_lo = 0xa5;
if (rp->ide_error == 0x5a || rp->ide_cyl_lo != 0xa5) {
printf ("\n");
return;
}
/* test if controller will reset */
if (idereset(sc) != 0) {
delay (500000);
if (idereset(sc) != 0) {
printf (" IDE controller did not reset\n");
return;
}
}
/* Dummy up the unit structures */
sc->sc_ide[0].sc_dev.dv_parent = (void *) sc;
sc->sc_ide[1].sc_dev.dv_parent = (void *) sc;
#if 0 /* Amiga ROM does this; it also takes a lot of time on the Seacrate */
/* Execute a controller only command. */
if (idecommand(&sc->sc_ide[0], 0, 0, 0, 0, IDEC_DIAGNOSE) != 0 ||
wait_for_unbusy(sc) != 0) {
printf (" ide attach failed\n");
return;
}
#endif
#ifdef DEBUG
if (ide_debug)
ide_dump_regs(rp);
#endif
idereset(sc);
for (i = 0; i < 2; ++i) {
rp->ide_sdh = 0xa0 | (i << 4);
sc->sc_ide[i].sc_drive = i;
if ((rp->ide_status & IDES_READY) == 0)
continue;
sc->sc_ide[i].sc_flags |= IDEF_ALIVE;
rp->ide_ctlr = 0;
}
printf ("\n");
sc->sc_link.adapter_softc = sc;
sc->sc_link.adapter_target = 7;
sc->sc_link.adapter = &idesc_scsiswitch;
sc->sc_link.device = &idesc_scsidev;
sc->sc_link.openings = 1;
TAILQ_INIT(&sc->sc_xslist);
sc->sc_isr.isr_intr = idesc_intr;
sc->sc_isr.isr_arg = sc;
sc->sc_isr.isr_ipl = 2;
add_isr (&sc->sc_isr);
/*
* attach all "scsi" units on us
*/
config_found(dp, &sc->sc_link, idescprint);
}
/*
* print diag if pnp is NULL else just extra
*/
int
idescprint(auxp, pnp)
void *auxp;
char *pnp;
{
if (pnp == NULL)
return(UNCONF);
return(QUIET);
}
/*
* used by specific ide controller
*
*/
int
ide_scsicmd(xs)
struct scsi_xfer *xs;
{
struct ide_pending *pendp;
struct idec_softc *dev;
struct scsi_link *slp;
int flags, s;
slp = xs->sc_link;
dev = slp->adapter_softc;
flags = xs->flags;
if (flags & SCSI_DATA_UIO)
panic("ide: scsi data uio requested");
if (dev->sc_xs && flags & SCSI_POLL)
panic("ide_scsicmd: busy");
s = splbio();
pendp = &dev->sc_xsstore[slp->target][slp->lun];
if (pendp->xs) {
splx(s);
return(TRY_AGAIN_LATER);
}
if (dev->sc_xs) {
pendp->xs = xs;
TAILQ_INSERT_TAIL(&dev->sc_xslist, pendp, link);
splx(s);
return(SUCCESSFULLY_QUEUED);
}
pendp->xs = NULL;
dev->sc_xs = xs;
splx(s);
/*
* nothing is pending do it now.
*/
ide_donextcmd(dev);
if (flags & SCSI_POLL)
return(COMPLETE);
return(SUCCESSFULLY_QUEUED);
}
/*
* entered with dev->sc_xs pointing to the next xfer to perform
*/
void
ide_donextcmd(dev)
struct idec_softc *dev;
{
struct scsi_xfer *xs;
struct scsi_link *slp;
int flags, stat;
xs = dev->sc_xs;
slp = xs->sc_link;
flags = xs->flags;
if (flags & SCSI_RESET)
idereset(dev);
dev->sc_stat[0] = -1;
/* Weed out invalid targets & LUNs here */
if (slp->target > 1 || slp->lun != 0) {
ide_scsidone(dev, -1);
return;
}
if (flags & SCSI_POLL || ide_no_int)
stat = ideicmd(dev, slp->target, xs->cmd, xs->cmdlen,
xs->data, xs->datalen);
else if (idego(dev, xs) == 0)
return;
else
stat = dev->sc_stat[0];
ide_scsidone(dev, stat);
}
void
ide_scsidone(dev, stat)
struct idec_softc *dev;
int stat;
{
struct ide_pending *pendp;
struct scsi_xfer *xs;
int s, donext;
xs = dev->sc_xs;
#ifdef DIAGNOSTIC
if (xs == NULL)
panic("ide_scsidone");
#endif
/*
* is this right?
*/
xs->status = stat;
if (stat == 0)
xs->resid = 0;
else {
switch(stat) {
case SCSI_CHECK:
if ((stat = idegetsense(dev, xs)) != 0)
goto bad_sense;
xs->error = XS_SENSE;
break;
case SCSI_BUSY:
xs->error = XS_BUSY;
break;
bad_sense:
default:
xs->error = XS_DRIVER_STUFFUP;
QPRINTF(("ide_scsicmd() bad %x\n", stat));
break;
}
}
xs->flags |= ITSDONE;
/*
* grab next command before scsi_done()
* this way no single device can hog scsi resources.
*/
s = splbio();
pendp = dev->sc_xslist.tqh_first;
if (pendp == NULL) {
donext = 0;
dev->sc_xs = NULL;
} else {
donext = 1;
TAILQ_REMOVE(&dev->sc_xslist, pendp, link);
dev->sc_xs = pendp->xs;
pendp->xs = NULL;
}
splx(s);
scsi_done(xs);
if (donext)
ide_donextcmd(dev);
}
int
idegetsense(dev, xs)
struct idec_softc *dev;
struct scsi_xfer *xs;
{
struct scsi_sense rqs;
struct scsi_link *slp;
slp = xs->sc_link;
rqs.opcode = REQUEST_SENSE;
rqs.byte2 = slp->lun << 5;
#ifdef not_yet
rqs.length = xs->req_sense_length ? xs->req_sense_length :
sizeof(xs->sense);
#else
rqs.length = sizeof(xs->sense);
#endif
rqs.unused[0] = rqs.unused[1] = rqs.control = 0;
return(ideicmd(dev, slp->target, &rqs, sizeof(rqs), &xs->sense,
rqs.length));
}
#ifdef DEBUG
void
ide_dump_regs(regs)
ide_regmap_p regs;
{
printf ("ide regs: %04x %02x %02x %02x %02x %02x %02x %02x\n",
regs->ide_data, regs->ide_error, regs->ide_seccnt,
regs->ide_sector, regs->ide_cyl_lo, regs->ide_cyl_hi,
regs->ide_sdh, regs->ide_command);
}
#endif
int
idereset(sc)
struct idec_softc *sc;
{
return (0);
}
int
idewait (sc, mask)
struct idec_softc *sc;
int mask;
{
ide_regmap_p regs = sc->sc_cregs;
int timeout = 0;
if ((regs->ide_status & IDES_BUSY) == 0 &&
(regs->ide_status & mask) == mask)
return (0);
#ifdef DEBUG
if (ide_debug)
printf ("idewait busy: %02x\n", regs->ide_status);
#endif
for (;;) {
if ((regs->ide_status & IDES_BUSY) == 0 &&
(regs->ide_status & mask) == mask)
break;
if (regs->ide_status & IDES_ERR)
break;
if (++timeout > 10000) {
printf ("idewait timeout %02x\n", regs->ide_status);
return (-1);
}
delay (1000);
}
if (regs->ide_status & IDES_ERR)
printf ("idewait: error %02x %02x\n", regs->ide_error,
regs->ide_status);
#ifdef DEBUG
else if (ide_debug)
printf ("idewait delay %d %02x\n", timeout, regs->ide_status);
#endif
return (regs->ide_status & IDES_ERR);
}
int
idecommand (ide, cylin, head, sector, count, cmd)
struct ide_softc *ide;
int cylin, head, sector, count;
int cmd;
{
struct idec_softc *idec = (void *)ide->sc_dev.dv_parent;
ide_regmap_p regs = idec->sc_cregs;
int stat;
#ifdef DEBUG
if (ide_debug)
printf ("idecommand: cmd = %02x\n", cmd);
#endif
if (wait_for_unbusy(idec) < 0)
return (-1);
regs->ide_sdh = 0xa0 | (ide->sc_drive << 4) | head;
if (cmd == IDEC_DIAGNOSE || cmd == IDEC_IDC)
stat = wait_for_unbusy(idec);
else
stat = idewait(idec, IDES_READY);
if (stat < 0) printf ("idecommand:%d stat %d\n", ide->sc_drive, stat);
if (stat < 0)
return (-1);
regs->ide_precomp = 0;
regs->ide_cyl_lo = cylin;
regs->ide_cyl_hi = cylin >> 8;
regs->ide_sector = sector;
regs->ide_seccnt = count;
regs->ide_command = cmd;
return (0);
}
int
idegetctlr(dev)
struct ide_softc *dev;
{
struct idec_softc *idec = (void *)dev->sc_dev.dv_parent;
ide_regmap_p regs = idec->sc_cregs;
char tb[DEV_BSIZE];
short *tbp = (short *) tb;
int i;
if (idecommand(dev, 0, 0, 0, 0, IDEC_READP) != 0 ||
wait_for_drq(idec) != 0) {
return (-1);
} else {
for (i = 0; i < DEV_BSIZE / 2; ++i)
*tbp++ = ntohs(regs->ide_data);
for (i = 0; i < DEV_BSIZE; i += 2) {
char temp;
temp = tb[i];
tb[i] = tb[i + 1];
tb[i + 1] = temp;
}
bcopy (tb, &dev->sc_params, sizeof (struct ideparams));
dev->sc_sectors = dev->sc_params.idep_sectors;
dev->sc_secpercyl = dev->sc_sectors *
dev->sc_params.idep_heads;
}
return (0);
}
int
ideiread(ide, block, buf, nblks)
struct ide_softc *ide;
long block;
u_char *buf;
int nblks;
{
int cylin, head, sector;
int stat;
u_short *bufp = (u_short *) buf;
int i;
struct idec_softc *idec = (void *) ide->sc_dev.dv_parent;
ide_regmap_p regs = idec->sc_cregs;
cylin = block / ide->sc_secpercyl;
head = (block % ide->sc_secpercyl) / ide->sc_sectors;
sector = block % ide->sc_sectors + 1;
stat = idecommand(ide, cylin, head, sector, nblks, IDEC_READ);
if (stat != 0)
return (-1);
while (nblks--) {
if (wait_for_drq(idec) != 0)
return (-1);
for (i = 0; i < DEV_BSIZE / 2 / 16; ++i) {
*bufp++ = regs->ide_data;
*bufp++ = regs->ide_data;
*bufp++ = regs->ide_data;
*bufp++ = regs->ide_data;
*bufp++ = regs->ide_data;
*bufp++ = regs->ide_data;
*bufp++ = regs->ide_data;
*bufp++ = regs->ide_data;
*bufp++ = regs->ide_data;
*bufp++ = regs->ide_data;
*bufp++ = regs->ide_data;
*bufp++ = regs->ide_data;
*bufp++ = regs->ide_data;
*bufp++ = regs->ide_data;
*bufp++ = regs->ide_data;
*bufp++ = regs->ide_data;
}
}
idec->sc_stat[0] = 0;
return (0);
}
int
ideiwrite(ide, block, buf, nblks)
struct ide_softc *ide;
long block;
u_char *buf;
int nblks;
{
int cylin, head, sector;
int stat;
u_short *bufp = (u_short *) buf;
int i;
struct idec_softc *idec = (void *) ide->sc_dev.dv_parent;
ide_regmap_p regs = idec->sc_cregs;
cylin = block / ide->sc_secpercyl;
head = (block % ide->sc_secpercyl) / ide->sc_sectors;
sector = block % ide->sc_sectors + 1;
stat = idecommand(ide, cylin, head, sector, nblks, IDEC_WRITE);
if (stat != 0)
return (-1);
while (nblks--) {
if (wait_for_drq(idec) != 0)
return (-1);
for (i = 0; i < DEV_BSIZE / 2 / 16; ++i) {
regs->ide_data = *bufp++;
regs->ide_data = *bufp++;
regs->ide_data = *bufp++;
regs->ide_data = *bufp++;
regs->ide_data = *bufp++;
regs->ide_data = *bufp++;
regs->ide_data = *bufp++;
regs->ide_data = *bufp++;
regs->ide_data = *bufp++;
regs->ide_data = *bufp++;
regs->ide_data = *bufp++;
regs->ide_data = *bufp++;
regs->ide_data = *bufp++;
regs->ide_data = *bufp++;
regs->ide_data = *bufp++;
regs->ide_data = *bufp++;
}
if (wait_for_unbusy(idec) != 0)
printf ("ideiwrite: timeout waiting for unbusy\n");
}
idec->sc_stat[0] = 0;
return (0);
}
int
ideicmd(dev, target, cbuf, clen, buf, len)
struct idec_softc *dev;
int target;
void *cbuf;
int clen;
void *buf;
int len;
{
struct ide_softc *ide;
int i;
int lba;
int nblks;
struct scsi_inquiry_data *inqbuf;
struct {
struct scsi_mode_header header;
struct scsi_blk_desc blk_desc;
union disk_pages pages;
} *mdsnbuf;
#ifdef DEBUG
if (ide_debug > 1)
printf ("ideicmd: target %d cmd %02x\n", target,
*((u_char *)cbuf));
#endif
if (target > 1)
return (-1); /* invalid unit */
ide = &dev->sc_ide[target];
if ((ide->sc_flags & IDEF_ALIVE) == 0)
return (-1);
if (*((u_char *)cbuf) != REQUEST_SENSE)
ide->sc_error = 0;
switch (*((u_char *)cbuf)) {
case TEST_UNIT_READY:
dev->sc_stat[0] = 0;
return (0);
case INQUIRY:
dev->sc_stat[0] = idegetctlr(ide);
if (dev->sc_stat[0] != 0)
return (dev->sc_stat[0]);
inqbuf = (void *) buf;
bzero (buf, len);
inqbuf->device = 0; /* XXX fixed disk */
inqbuf->dev_qual2 = 0; /* XXX check RMB */
inqbuf->version = 2;
inqbuf->response_format = 2;
inqbuf->additional_length = 31;
for (i = 0; i < 8; ++i)
inqbuf->vendor[i] = ide->sc_params.idep_model[i];
for (i = 0; i < 16; ++i)
inqbuf->product[i] = ide->sc_params.idep_model[i+8];
for (i = 0; i < 4; ++i)
inqbuf->revision[i] = ide->sc_params.idep_rev[i];
return (0);
case READ_CAPACITY:
*((long *)buf) = ide->sc_params.idep_sectors *
ide->sc_params.idep_heads *
ide->sc_params.idep_fixedcyl - 1;
*((long *)buf + 1) = 512; /* XXX 512 byte blocks */
dev->sc_stat[0] = 0;
return (0);
case READ_BIG:
lba = *((long *)(cbuf + 2));
nblks = *((u_short *)(cbuf + 7));
return (ideiread(ide, lba, buf, nblks));
case READ_COMMAND:
lba = *((long *)cbuf) & 0x001fffff;
nblks = *((u_char *)(cbuf + 4));
if (nblks == 0)
nblks = 256;
return (ideiread(ide, lba, buf, nblks));
case WRITE_BIG:
lba = *((long *)(cbuf + 2));
nblks = *((u_short *)(cbuf + 7));
return (ideiwrite(ide, lba, buf, nblks));
case WRITE_COMMAND:
lba = *((long *)cbuf) & 0x001fffff;
nblks = *((u_char *)(cbuf + 4));
if (nblks == 0)
nblks = 256;
return (ideiwrite(ide, lba, buf, nblks));
case PREVENT_ALLOW:
case START_STOP: /* and LOAD */
dev->sc_stat[0] = 0;
return (0);
case MODE_SENSE:
mdsnbuf = (void*) buf;
bzero(buf, *((u_char *)cbuf + 4));
switch (*((u_char *)cbuf + 2) & 0x3f) {
case 4:
mdsnbuf->header.data_length = 27;
mdsnbuf->header.blk_desc_len = 8;
mdsnbuf->blk_desc.blklen[1] = 512 >> 8;
mdsnbuf->pages.rigid_geometry.pg_code = 4;
mdsnbuf->pages.rigid_geometry.pg_length = 16;
_lto3b(ide->sc_params.idep_fixedcyl,
mdsnbuf->pages.rigid_geometry.ncyl);
mdsnbuf->pages.rigid_geometry.nheads =
ide->sc_params.idep_heads;
dev->sc_stat[0] = 0;
return (0);
default:
printf ("ide: mode sense page %x not simulated\n",
*((u_char *)cbuf + 2) & 0x3f);
return (-1);
}
case REQUEST_SENSE:
/* convert sc_error to SCSI sense */
bzero (buf, *((u_char *)cbuf + 4));
*((u_char *) buf) = 0x70;
*((u_char *) buf + 7) = 10;
i = 0;
while (sense_convert[i].ide_err) {
if (sense_convert[i].ide_err & ide->sc_error)
break;
++i;
}
*((u_char *) buf + 2) = sense_convert[i].scsi_sense_key;
*((u_char *) buf + 12) = sense_convert[i].scsi_sense_qual;
dev->sc_stat[0] = 0;
printf("ide: request sense %02x -> %02x %02x\n", ide->sc_error,
sense_convert[i].scsi_sense_key, sense_convert[i].scsi_sense_qual);
return (0);
case 0x01 /*REWIND*/:
case 0x04 /*CMD_FORMAT_UNIT*/:
case 0x05 /*READ_BLOCK_LIMITS*/:
case REASSIGN_BLOCKS:
case 0x10 /*WRITE_FILEMARKS*/:
case 0x11 /*SPACE*/:
case MODE_SELECT:
default:
printf ("ide: unhandled SCSI command %02x\n", *((u_char *)cbuf));
ide->sc_error = 0x04;
dev->sc_stat[0] = SCSI_CHECK;
return (SCSI_CHECK);
}
}
int
idego(dev, xs)
struct idec_softc *dev;
struct scsi_xfer *xs;
{
struct ide_softc *ide = &dev->sc_ide[xs->sc_link->target];
long lba;
int nblks;
#if 0
cdb->cdb[1] |= unit << 5;
#endif
ide->sc_buf = xs->data;
ide->sc_bcount = xs->datalen;
#ifdef DEBUG
if (ide_debug > 1)
printf ("ide_go: %02x\n", xs->cmd->opcode);
#endif
if (xs->cmd->opcode != READ_COMMAND && xs->cmd->opcode != READ_BIG &&
xs->cmd->opcode != WRITE_COMMAND && xs->cmd->opcode != WRITE_BIG) {
ideicmd (dev, xs->sc_link->target, xs->cmd, xs->cmdlen,
xs->data, xs->datalen);
return (1);
}
switch (xs->cmd->opcode) {
case READ_COMMAND:
case WRITE_COMMAND:
lba = *((long *)xs->cmd) & 0x001fffff;
nblks = xs->cmd->bytes[3];
if (nblks == 0)
nblks = 256;
break;
case READ_BIG:
case WRITE_BIG:
lba = *((long *)&xs->cmd->bytes[1]);
nblks = *((short *)&xs->cmd->bytes[6]);
break;
default:
panic ("idego bad SCSI command");
}
ide->sc_blknum = lba;
ide->sc_blkcnt = nblks;
ide->sc_skip = ide->sc_mskip = 0;
dev->sc_flags &= ~IDECF_READ;
if (xs->cmd->opcode == READ_COMMAND || xs->cmd->opcode == READ_BIG)
dev->sc_flags |= IDECF_READ;
dev->sc_cur = ide;
return (idestart (dev));
}
int
idestart(dev)
struct idec_softc *dev;
{
long blknum, cylin, head, sector;
int command, count;
struct ide_softc *ide = dev->sc_cur;
short *bf;
int i;
ide_regmap_p regs = dev->sc_cregs;
dev->sc_flags |= IDECF_ACTIVE;
blknum = ide->sc_blknum + ide->sc_skip;
if (ide->sc_mskip == 0) {
ide->sc_mbcount = ide->sc_bcount;
}
cylin = blknum / ide->sc_secpercyl;
head = (blknum % ide->sc_secpercyl) / ide->sc_sectors;
sector = blknum % ide->sc_sectors;
++sector;
if (ide->sc_mskip == 0 || dev->sc_flags & IDECF_SINGLE) {
count = howmany(ide->sc_mbcount, DEV_BSIZE);
command = (dev->sc_flags & IDECF_READ) ?
IDEC_READ : IDEC_WRITE;
if (idecommand(ide, cylin, head, sector, count, command) != 0) {
printf ("idestart: timeout waiting for unbusy\n");
#if 0
bp->b_error = EINVAL;
bp->b_flags |= B_ERROR;
idfinish(&dev->sc_ide[0], bp);
#endif
ide_scsidone(dev, dev->sc_stat[0]);
return (1);
}
}
dev->sc_stat[0] = 0;
if (dev->sc_flags & IDECF_READ)
return (0);
if (wait_for_drq(dev) < 0) {
printf ("idestart: timeout waiting for drq\n");
}
#define W1 (regs->ide_data = *bf++)
for (i = 0, bf = (short *) (ide->sc_buf + ide->sc_skip * DEV_BSIZE);
i < DEV_BSIZE / 2 / 16; ++i) {
W1; W1; W1; W1; W1; W1; W1; W1;
W1; W1; W1; W1; W1; W1; W1; W1;
}
return (0);
}
int
idesc_intr(arg)
void *arg;
{
struct idec_softc *dev = arg;
ide_regmap_p regs;
struct ide_softc *ide;
short dummy;
short *bf;
int i;
#if 0
if (idesc_cd.cd_ndevs == 0 || (dev = idesc_cd.cd_devs[0]) == NULL)
return (0);
#endif
regs = dev->sc_cregs;
if (dev->sc_flags & IDECF_A1200) {
if (*dev->sc_a1200 & 0x80) {
#if 0
printf ("idesc_intr: A1200 interrupt %x\n", *dev->sc_a1200);
#endif
dummy = regs->ide_status; /* XXX */
*dev->sc_a1200 = 0x7c | (*dev->sc_a1200 & 0x03);
}
else
return (0);
} else {
if (regs->ide_intpnd >= 0)
return (0);
dummy = regs->ide_status;
}
#ifdef DEBUG
if (ide_debug)
printf ("idesc_intr: %02x\n", dummy);
#endif
if ((dev->sc_flags & IDECF_ACTIVE) == 0)
return (1);
dev->sc_flags &= ~IDECF_ACTIVE;
if (wait_for_unbusy(dev) < 0)
printf ("idesc_intr: timeout waiting for unbusy\n");
ide = dev->sc_cur;
if (dummy & IDES_ERR) {
dev->sc_stat[0] = SCSI_CHECK;
ide->sc_error = regs->ide_error;
printf("idesc_intr: error %02x, %02x\n", dev->sc_stat[1], dummy);
ide_scsidone(dev, dev->sc_stat[0]);
}
if (dev->sc_flags & IDECF_READ) {
#define R2 (*bf++ = regs->ide_data)
bf = (short *) (ide->sc_buf + ide->sc_skip * DEV_BSIZE);
if (wait_for_drq(dev) != 0)
printf ("idesc_intr: read error detected late\n");
for (i = 0; i < DEV_BSIZE / 2 / 16; ++i) {
R2; R2; R2; R2; R2; R2; R2; R2;
R2; R2; R2; R2; R2; R2; R2; R2;
}
}
ide->sc_skip++;
ide->sc_mskip++;
ide->sc_bcount -= DEV_BSIZE;
ide->sc_mbcount -= DEV_BSIZE;
#ifdef DEBUG
if (ide_debug)
printf ("idesc_intr: sc_bcount %ld\n", ide->sc_bcount);
#endif
if (ide->sc_bcount == 0)
ide_scsidone(dev, dev->sc_stat[0]);
else
/* Check return value here? */
idestart (dev);
return (1);
}