diff options
Diffstat (limited to 'sys/dev/ata/ata-all.c')
-rw-r--r-- | sys/dev/ata/ata-all.c | 1479 |
1 files changed, 1479 insertions, 0 deletions
diff --git a/sys/dev/ata/ata-all.c b/sys/dev/ata/ata-all.c new file mode 100644 index 0000000..c7cc11f --- /dev/null +++ b/sys/dev/ata/ata-all.c @@ -0,0 +1,1479 @@ +/*- + * Copyright (c) 1998,1999,2000,2001,2002 Søren Schmidt <sos@FreeBSD.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer, + * without modification, immediately at the beginning of the file. + * 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. 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. + * + * $FreeBSD$ + */ + +#include "opt_ata.h" +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/ata.h> +#include <sys/kernel.h> +#include <sys/conf.h> +#include <sys/disk.h> +#include <sys/module.h> +#include <sys/bus.h> +#include <sys/bio.h> +#include <sys/malloc.h> +#include <sys/devicestat.h> +#include <sys/stdint.h> +#include <sys/sysctl.h> +#include <machine/stdarg.h> +#include <machine/resource.h> +#include <machine/bus.h> +#include <sys/rman.h> +#ifdef __alpha__ +#include <machine/md_var.h> +#endif +#include <dev/ata/ata-all.h> +#include <dev/ata/ata-disk.h> +#include <dev/ata/ata-raid.h> +#include <dev/ata/atapi-all.h> + +/* device structures */ +static d_ioctl_t ataioctl; +static struct cdevsw ata_cdevsw = { + /* open */ nullopen, + /* close */ nullclose, + /* read */ noread, + /* write */ nowrite, + /* ioctl */ ataioctl, + /* poll */ nopoll, + /* mmap */ nommap, + /* strategy */ nostrategy, + /* name */ "ata", + /* maj */ 159, + /* dump */ nodump, + /* psize */ nopsize, + /* flags */ 0, +}; + +/* prototypes */ +static void ata_boot_attach(void); +static void ata_intr(void *); +static int ata_getparam(struct ata_device *, u_int8_t); +static int ata_service(struct ata_channel *); +static void bswap(int8_t *, int); +static void btrim(int8_t *, int); +static void bpack(int8_t *, int8_t *, int); +static void ata_change_mode(struct ata_device *, int); +static u_int8_t ata_drawersensor(struct ata_device *, int, u_int8_t, u_int8_t); + +/* sysctl vars */ +SYSCTL_NODE(_hw, OID_AUTO, ata, CTLFLAG_RD, 0, "ATA driver parameters"); + +/* global vars */ +devclass_t ata_devclass; + +/* local vars */ +static struct intr_config_hook *ata_delayed_attach = NULL; +static MALLOC_DEFINE(M_ATA, "ATA generic", "ATA driver generic layer"); + +/* misc defines */ +#define DEV_ATAPIALL defined(DEV_ATAPICD) || defined(DEV_ATAPIFD) || \ + defined(DEV_ATAPIST) || defined(DEV_ATAPICAM) + +int +ata_probe(device_t dev) +{ + struct ata_channel *ch; + int rid; + + if (!dev || !(ch = device_get_softc(dev))) + return ENXIO; + + if (ch->r_io || ch->r_altio || ch->r_irq) + return EEXIST; + + /* initialize the softc basics */ + ch->active = ATA_IDLE; + ch->dev = dev; + + rid = ATA_IOADDR_RID; + ch->r_io = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0, + ATA_IOSIZE, RF_ACTIVE); + if (!ch->r_io) + goto failure; + + rid = ATA_ALTADDR_RID; + ch->r_altio = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0, + ATA_ALTIOSIZE, RF_ACTIVE); + if (!ch->r_altio) + goto failure; + + rid = ATA_BMADDR_RID; + ch->r_bmio = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0, + ATA_BMIOSIZE, RF_ACTIVE); + if (bootverbose) + ata_printf(ch, -1, "iobase=0x%04x altiobase=0x%04x bmaddr=0x%04x\n", + (int)rman_get_start(ch->r_io), + (int)rman_get_start(ch->r_altio), + (ch->r_bmio) ? (int)rman_get_start(ch->r_bmio) : 0); + + ata_reset(ch); + + ch->device[MASTER].channel = ch; + ch->device[MASTER].unit = ATA_MASTER; + ch->device[MASTER].mode = ATA_PIO; + ch->device[SLAVE].channel = ch; + ch->device[SLAVE].unit = ATA_SLAVE; + ch->device[SLAVE].mode = ATA_PIO; + TAILQ_INIT(&ch->ata_queue); + TAILQ_INIT(&ch->atapi_queue); + return 0; + +failure: + if (ch->r_io) + bus_release_resource(dev, SYS_RES_IOPORT, ATA_IOADDR_RID, ch->r_io); + if (ch->r_altio) + bus_release_resource(dev, SYS_RES_IOPORT, ATA_ALTADDR_RID, ch->r_altio); + if (ch->r_bmio) + bus_release_resource(dev, SYS_RES_IOPORT, ATA_BMADDR_RID, ch->r_bmio); + if (bootverbose) + ata_printf(ch, -1, "probe allocation failed\n"); + return ENXIO; +} + +int +ata_attach(device_t dev) +{ + struct ata_channel *ch; + int error, rid; + + if (!dev || !(ch = device_get_softc(dev))) + return ENXIO; + + rid = ATA_IRQ_RID; + ch->r_irq = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0, 1, + RF_SHAREABLE | RF_ACTIVE); + if (!ch->r_irq) { + ata_printf(ch, -1, "unable to allocate interrupt\n"); + return ENXIO; + } + if ((error = bus_setup_intr(dev, ch->r_irq, INTR_TYPE_BIO | INTR_ENTROPY, + ata_intr, ch, &ch->ih))) { + ata_printf(ch, -1, "unable to setup interrupt\n"); + return error; + } + + /* + * do not attach devices if we are in early boot, this is done later + * when interrupts are enabled by a hook into the boot process. + * otherwise attach what the probe has found in ch->devices. + */ + if (!ata_delayed_attach) { + if (ch->devices & ATA_ATA_SLAVE) + if (ata_getparam(&ch->device[SLAVE], ATA_C_ATA_IDENTIFY)) + ch->devices &= ~ATA_ATA_SLAVE; + if (ch->devices & ATA_ATAPI_SLAVE) + if (ata_getparam(&ch->device[SLAVE], ATA_C_ATAPI_IDENTIFY)) + ch->devices &= ~ATA_ATAPI_SLAVE; + if (ch->devices & ATA_ATA_MASTER) + if (ata_getparam(&ch->device[MASTER], ATA_C_ATA_IDENTIFY)) + ch->devices &= ~ATA_ATA_MASTER; + if (ch->devices & ATA_ATAPI_MASTER) + if (ata_getparam(&ch->device[MASTER], ATA_C_ATAPI_IDENTIFY)) + ch->devices &= ~ATA_ATAPI_MASTER; +#ifdef DEV_ATADISK + if (ch->devices & ATA_ATA_MASTER) + ad_attach(&ch->device[MASTER]); + if (ch->devices & ATA_ATA_SLAVE) + ad_attach(&ch->device[SLAVE]); +#endif +#if DEV_ATAPIALL + if (ch->devices & ATA_ATAPI_MASTER) + atapi_attach(&ch->device[MASTER]); + if (ch->devices & ATA_ATAPI_SLAVE) + atapi_attach(&ch->device[SLAVE]); +#endif +#ifdef DEV_ATAPICAM + atapi_cam_attach_bus(ch); +#endif + } + return 0; +} + +int +ata_detach(device_t dev) +{ + struct ata_channel *ch; + int s; + + if (!dev || !(ch = device_get_softc(dev)) || + !ch->r_io || !ch->r_altio || !ch->r_irq) + return ENXIO; + + /* make sure channel is not busy */ + ATA_SLEEPLOCK_CH(ch, ATA_CONTROL); + + s = splbio(); +#ifdef DEV_ATADISK + if (ch->devices & ATA_ATA_MASTER && ch->device[MASTER].driver) + ad_detach(&ch->device[MASTER], 1); + if (ch->devices & ATA_ATA_SLAVE && ch->device[SLAVE].driver) + ad_detach(&ch->device[SLAVE], 1); +#endif +#if DEV_ATAPIALL + if (ch->devices & ATA_ATAPI_MASTER && ch->device[MASTER].driver) + atapi_detach(&ch->device[MASTER]); + if (ch->devices & ATA_ATAPI_SLAVE && ch->device[SLAVE].driver) + atapi_detach(&ch->device[SLAVE]); +#endif +#ifdef DEV_ATAPICAM + atapi_cam_detach_bus(ch); +#endif + splx(s); + + if (ch->device[MASTER].param) { + free(ch->device[MASTER].param, M_ATA); + ch->device[MASTER].param = NULL; + } + if (ch->device[SLAVE].param) { + free(ch->device[SLAVE].param, M_ATA); + ch->device[SLAVE].param = NULL; + } + ch->device[MASTER].driver = NULL; + ch->device[SLAVE].driver = NULL; + ch->device[MASTER].mode = ATA_PIO; + ch->device[SLAVE].mode = ATA_PIO; + ch->devices = 0; + ata_dmafreetags(ch); + + bus_teardown_intr(dev, ch->r_irq, ch->ih); + bus_release_resource(dev, SYS_RES_IRQ, ATA_IRQ_RID, ch->r_irq); + if (ch->r_bmio) + bus_release_resource(dev, SYS_RES_IOPORT, ATA_BMADDR_RID, ch->r_bmio); + bus_release_resource(dev, SYS_RES_IOPORT, ATA_ALTADDR_RID, ch->r_altio); + bus_release_resource(dev, SYS_RES_IOPORT, ATA_IOADDR_RID, ch->r_io); + ch->r_io = NULL; + ch->r_altio = NULL; + ch->r_bmio = NULL; + ch->r_irq = NULL; + ATA_UNLOCK_CH(ch); + return 0; +} + +int +ata_resume(device_t dev) +{ + return ata_reinit(device_get_softc(dev)); +} + +static int +ataioctl(dev_t dev, u_long cmd, caddr_t addr, int32_t flag, struct thread *td) +{ + struct ata_cmd *iocmd = (struct ata_cmd *)addr; + struct ata_channel *ch; + device_t device = devclass_get_device(ata_devclass, iocmd->channel); + int error; + + if (cmd != IOCATA) + return ENOTTY; + + if (iocmd->channel < -1 || iocmd->device < -1 || iocmd->device > SLAVE) + return ENXIO; + + switch (iocmd->cmd) { + case ATAATTACH: + /* should enable channel HW on controller that can SOS XXX */ + error = ata_probe(device); + if (!error) + error = ata_attach(device); + return error; + + case ATADETACH: + error = ata_detach(device); + /* should disable channel HW on controller that can SOS XXX */ + return error; + + case ATAREINIT: + if (!device || !(ch = device_get_softc(device))) + return ENXIO; + ATA_SLEEPLOCK_CH(ch, ATA_ACTIVE); + if ((error = ata_reinit(ch))) + ATA_UNLOCK_CH(ch); + return error; + + case ATAGMODE: + if (!device || !(ch = device_get_softc(device))) + return ENXIO; + + if ((iocmd->device == MASTER || iocmd->device == -1) && + ch->device[MASTER].driver) + iocmd->u.mode.mode[MASTER] = ch->device[MASTER].mode; + else + iocmd->u.mode.mode[MASTER] = -1; + + if ((iocmd->device == SLAVE || iocmd->device == -1) && + ch->device[SLAVE].param) + iocmd->u.mode.mode[SLAVE] = ch->device[SLAVE].mode; + else + iocmd->u.mode.mode[SLAVE] = -1; + return 0; + + case ATASMODE: + if (!device || !(ch = device_get_softc(device))) + return ENXIO; + + if ((iocmd->device == MASTER || iocmd->device == -1) && + iocmd->u.mode.mode[MASTER] >= 0 && ch->device[MASTER].param) { + ata_change_mode(&ch->device[MASTER],iocmd->u.mode.mode[MASTER]); + iocmd->u.mode.mode[MASTER] = ch->device[MASTER].mode; + } + else + iocmd->u.mode.mode[MASTER] = -1; + + if ((iocmd->device == SLAVE || iocmd->device == -1) && + iocmd->u.mode.mode[SLAVE] >= 0 && ch->device[SLAVE].param) { + ata_change_mode(&ch->device[SLAVE], iocmd->u.mode.mode[SLAVE]); + iocmd->u.mode.mode[SLAVE] = ch->device[SLAVE].mode; + } + else + iocmd->u.mode.mode[SLAVE] = -1; + return 0; + + case ATAGPARM: + if (!device || !(ch = device_get_softc(device))) + return ENXIO; + + iocmd->u.param.type[MASTER] = + ch->devices & (ATA_ATA_MASTER | ATA_ATAPI_MASTER); + iocmd->u.param.type[SLAVE] = + ch->devices & (ATA_ATA_SLAVE | ATA_ATAPI_SLAVE); + + if (ch->device[MASTER].name) + strcpy(iocmd->u.param.name[MASTER], ch->device[MASTER].name); + if (ch->device[SLAVE].name) + strcpy(iocmd->u.param.name[SLAVE], ch->device[SLAVE].name); + + if (ch->device[MASTER].param) + bcopy(ch->device[MASTER].param, &iocmd->u.param.params[MASTER], + sizeof(struct ata_params)); + if (ch->device[SLAVE].param) + bcopy(ch->device[SLAVE].param, &iocmd->u.param.params[SLAVE], + sizeof(struct ata_params)); + return 0; + + case ATAENCSTAT: { + struct ata_device *atadev; + u_int8_t id1, id2, cnt, div; + int fan, temp; + + if (!device || !(ch = device_get_softc(device))) + return ENXIO; + + ATA_SLEEPLOCK_CH(ch, ATA_ACTIVE); + + if (iocmd->device == SLAVE) + atadev = &ch->device[SLAVE]; + else + atadev = &ch->device[MASTER]; + + ata_drawersensor(atadev, 1, 0x4e, 0); + id1 = ata_drawersensor(atadev, 0, 0x4f, 0); + ata_drawersensor(atadev, 1, 0x4e, 0x80); + id2 = ata_drawersensor(atadev, 0, 0x4f, 0); + if (id1 != 0xa3 || id2 != 0x5c) { + ATA_UNLOCK_CH(ch); + return ENXIO; + } + + div = 1 << (((ata_drawersensor(atadev, 0, 0x5d, 0)&0x20)>>3) + + ((ata_drawersensor(atadev, 0, 0x47, 0)&0x30)>>4) + 1); + cnt = ata_drawersensor(atadev, 0, 0x28, 0); + if (cnt == 0xff) + fan = 0; + else + fan = 1350000 / cnt / div; + ata_drawersensor(atadev, 1, 0x4e, 0x01); + temp = (ata_drawersensor(atadev, 0, 0x50, 0) * 10) + + (ata_drawersensor(atadev, 0, 0x50, 0) & 0x80 ? 5 : 0); + + iocmd->u.enclosure.fan = fan; + iocmd->u.enclosure.temp = temp; + iocmd->u.enclosure.v05 = ata_drawersensor(atadev, 0, 0x23, 0) * 27; + iocmd->u.enclosure.v12 = ata_drawersensor(atadev, 0, 0x24, 0) * 61; + + ATA_UNLOCK_CH(ch); + return 0; + } + +#ifdef DEV_ATADISK + case ATARAIDREBUILD: + return ata_raid_rebuild(iocmd->channel); + + case ATARAIDCREATE: + return ata_raid_create(&iocmd->u.raid_setup); + + case ATARAIDDELETE: + return ata_raid_delete(iocmd->channel); + + case ATARAIDSTATUS: + return ata_raid_status(iocmd->channel, &iocmd->u.raid_status); +#endif +#if DEV_ATAPIALL + case ATAPICMD: { + struct ata_device *atadev; + caddr_t buf; + + if (!device || !(ch = device_get_softc(device))) + return ENXIO; + + if (!(atadev = &ch->device[iocmd->device]) || + !(ch->devices & (iocmd->device == MASTER ? + ATA_ATAPI_MASTER : ATA_ATAPI_SLAVE))) + return ENODEV; + + if (!(buf = malloc(iocmd->u.atapi.count, M_ATA, M_NOWAIT))) + return ENOMEM; + + if (iocmd->u.atapi.flags & ATAPI_CMD_WRITE) { + error = copyin(iocmd->u.atapi.data, buf, iocmd->u.atapi.count); + if (error) + return error; + } + error = atapi_queue_cmd(atadev, iocmd->u.atapi.ccb, + buf, iocmd->u.atapi.count, + (iocmd->u.atapi.flags == ATAPI_CMD_READ ? + ATPR_F_READ : 0) | ATPR_F_QUIET, + iocmd->u.atapi.timeout, NULL, NULL); + if (error) { + iocmd->u.atapi.error = error; + bcopy(&atadev->result, iocmd->u.atapi.sense_data, + sizeof(struct atapi_reqsense)); + error = 0; + } + else if (iocmd->u.atapi.flags & ATAPI_CMD_READ) + error = copyout(buf, iocmd->u.atapi.data, iocmd->u.atapi.count); + + free(buf, M_ATA); + return error; + } +#endif + default: + break; + } + return ENOTTY; +} + +static int +ata_getparam(struct ata_device *atadev, u_int8_t command) +{ + struct ata_params *ata_parm; + int retry = 0; + + if (!(ata_parm = malloc(sizeof(struct ata_params), M_ATA, M_NOWAIT))) { + ata_prtdev(atadev, "malloc for identify data failed\n"); + return -1; + } + + /* apparently some devices needs this repeated */ + do { + if (ata_command(atadev, command, 0, 0, 0, ATA_WAIT_INTR)) { + ata_prtdev(atadev, "%s identify failed\n", + command == ATA_C_ATAPI_IDENTIFY ? "ATAPI" : "ATA"); + free(ata_parm, M_ATA); + return -1; + } + if (retry++ > 4) { + ata_prtdev(atadev, "%s identify retries exceeded\n", + command == ATA_C_ATAPI_IDENTIFY ? "ATAPI" : "ATA"); + free(ata_parm, M_ATA); + return -1; + } + } while (ata_wait(atadev, ((command == ATA_C_ATAPI_IDENTIFY) ? + ATA_S_DRQ : (ATA_S_READY|ATA_S_DSC|ATA_S_DRQ)))); + ATA_INSW(atadev->channel->r_io, ATA_DATA, (int16_t *)ata_parm, + sizeof(struct ata_params)/sizeof(int16_t)); + + if (command == ATA_C_ATA_IDENTIFY || + !((ata_parm->model[0] == 'N' && ata_parm->model[1] == 'E') || + (ata_parm->model[0] == 'F' && ata_parm->model[1] == 'X') || + (ata_parm->model[0] == 'P' && ata_parm->model[1] == 'i'))) + bswap(ata_parm->model, sizeof(ata_parm->model)); + btrim(ata_parm->model, sizeof(ata_parm->model)); + bpack(ata_parm->model, ata_parm->model, sizeof(ata_parm->model)); + bswap(ata_parm->revision, sizeof(ata_parm->revision)); + btrim(ata_parm->revision, sizeof(ata_parm->revision)); + bpack(ata_parm->revision, ata_parm->revision, sizeof(ata_parm->revision)); + bswap(ata_parm->serial, sizeof(ata_parm->serial)); + btrim(ata_parm->serial, sizeof(ata_parm->serial)); + bpack(ata_parm->serial, ata_parm->serial, sizeof(ata_parm->serial)); + atadev->param = ata_parm; + return 0; +} + +static void +ata_boot_attach(void) +{ + struct ata_channel *ch; + int ctlr; + + if (ata_delayed_attach) { + config_intrhook_disestablish(ata_delayed_attach); + free(ata_delayed_attach, M_TEMP); + ata_delayed_attach = NULL; + } + + /* + * run through all ata devices and look for real ATA & ATAPI devices + * using the hints we found in the early probe, this avoids some of + * the delays probing of non-exsistent devices can cause. + */ + for (ctlr=0; ctlr<devclass_get_maxunit(ata_devclass); ctlr++) { + if (!(ch = devclass_get_softc(ata_devclass, ctlr))) + continue; + if (ch->devices & ATA_ATA_SLAVE) + if (ata_getparam(&ch->device[SLAVE], ATA_C_ATA_IDENTIFY)) + ch->devices &= ~ATA_ATA_SLAVE; + if (ch->devices & ATA_ATAPI_SLAVE) + if (ata_getparam(&ch->device[SLAVE], ATA_C_ATAPI_IDENTIFY)) + ch->devices &= ~ATA_ATAPI_SLAVE; + if (ch->devices & ATA_ATA_MASTER) + if (ata_getparam(&ch->device[MASTER], ATA_C_ATA_IDENTIFY)) + ch->devices &= ~ATA_ATA_MASTER; + if (ch->devices & ATA_ATAPI_MASTER) + if (ata_getparam(&ch->device[MASTER], ATA_C_ATAPI_IDENTIFY)) + ch->devices &= ~ATA_ATAPI_MASTER; + } + +#ifdef DEV_ATADISK + /* now we know whats there, do the real attach, first the ATA disks */ + for (ctlr=0; ctlr<devclass_get_maxunit(ata_devclass); ctlr++) { + if (!(ch = devclass_get_softc(ata_devclass, ctlr))) + continue; + if (ch->devices & ATA_ATA_MASTER) + ad_attach(&ch->device[MASTER]); + if (ch->devices & ATA_ATA_SLAVE) + ad_attach(&ch->device[SLAVE]); + } + ata_raid_attach(); +#endif + /* then the atapi devices */ + for (ctlr=0; ctlr<devclass_get_maxunit(ata_devclass); ctlr++) { + if (!(ch = devclass_get_softc(ata_devclass, ctlr))) + continue; +#if DEV_ATAPIALL + if (ch->devices & ATA_ATAPI_MASTER) + atapi_attach(&ch->device[MASTER]); + if (ch->devices & ATA_ATAPI_SLAVE) + atapi_attach(&ch->device[SLAVE]); +#endif +#ifdef DEV_ATAPICAM + atapi_cam_attach_bus(ch); +#endif + } +} + +static void +ata_intr(void *data) +{ + struct ata_channel *ch = (struct ata_channel *)data; + /* + * on PCI systems we might share an interrupt line with another + * device or our twin ATA channel, so call ch->intr_func to figure + * out if it is really an interrupt we should process here + */ + if (ch->intr_func && ch->intr_func(ch)) + return; + + /* if drive is busy it didn't interrupt */ + if (ATA_INB(ch->r_altio, ATA_ALTSTAT) & ATA_S_BUSY) { + DELAY(100); + if (!(ATA_INB(ch->r_altio, ATA_ALTSTAT) & ATA_S_DRQ)) + return; + } + + /* clear interrupt and get status */ + ch->status = ATA_INB(ch->r_io, ATA_STATUS); + + if (ch->status & ATA_S_ERROR) + ch->error = ATA_INB(ch->r_io, ATA_ERROR); + + /* find & call the responsible driver to process this interrupt */ + switch (ch->active) { +#ifdef DEV_ATADISK + case ATA_ACTIVE_ATA: + if (!ch->running || ad_interrupt(ch->running) == ATA_OP_CONTINUES) + return; + break; +#endif +#if DEV_ATAPIALL + case ATA_ACTIVE_ATAPI: + if (!ch->running || atapi_interrupt(ch->running) == ATA_OP_CONTINUES) + return; + break; +#endif + default: + if (ch->active & ATA_WAIT_INTR) + wakeup((caddr_t)ch); + } + + if (ch->active & ATA_CONTROL) { + ATA_FORCELOCK_CH(ch, ATA_CONTROL); + return; + } + + if ((ch->flags & ATA_QUEUED) && + ATA_INB(ch->r_altio, ATA_ALTSTAT) & ATA_S_SERVICE) { + ATA_FORCELOCK_CH(ch, ATA_ACTIVE); + if (ata_service(ch) == ATA_OP_CONTINUES) + return; + } + ATA_UNLOCK_CH(ch); + ch->running = NULL; + ata_start(ch); + return; +} + +void +ata_start(struct ata_channel *ch) +{ +#ifdef DEV_ATADISK + struct ad_request *ad_request; +#endif +#if DEV_ATAPIALL + struct atapi_request *atapi_request; +#endif + int s; + + if (!ATA_LOCK_CH(ch, ATA_ACTIVE)) + return; + + s = splbio(); +#ifdef DEV_ATADISK + /* find & call the responsible driver if anything on the ATA queue */ + if (TAILQ_EMPTY(&ch->ata_queue)) { + if (ch->devices & (ATA_ATA_MASTER) && ch->device[MASTER].driver) + ad_start(&ch->device[MASTER]); + if (ch->devices & (ATA_ATA_SLAVE) && ch->device[SLAVE].driver) + ad_start(&ch->device[SLAVE]); + } + if ((ad_request = TAILQ_FIRST(&ch->ata_queue))) { + TAILQ_REMOVE(&ch->ata_queue, ad_request, chain); + ch->active = ATA_ACTIVE_ATA; + ch->running = ad_request; + if (ad_transfer(ad_request) == ATA_OP_CONTINUES) { + splx(s); + return; + } + } + +#endif +#if DEV_ATAPIALL + /* find & call the responsible driver if anything on the ATAPI queue */ + if (TAILQ_EMPTY(&ch->atapi_queue)) { + if (ch->devices & (ATA_ATAPI_MASTER) && ch->device[MASTER].driver) + atapi_start(&ch->device[MASTER]); + if (ch->devices & (ATA_ATAPI_SLAVE) && ch->device[SLAVE].driver) + atapi_start(&ch->device[SLAVE]); + } + if ((atapi_request = TAILQ_FIRST(&ch->atapi_queue))) { + TAILQ_REMOVE(&ch->atapi_queue, atapi_request, chain); + ch->active = ATA_ACTIVE_ATAPI; + ch->running = atapi_request; + if (atapi_transfer(atapi_request) == ATA_OP_CONTINUES) { + splx(s); + return; + } + } +#endif + ATA_UNLOCK_CH(ch); + splx(s); +} + +void +ata_reset(struct ata_channel *ch) +{ + u_int8_t lsb, msb, ostat0, ostat1; + u_int8_t stat0 = 0, stat1 = 0; + int mask = 0, timeout; + + /* do we have any signs of ATA/ATAPI HW being present ? */ + ATA_OUTB(ch->r_io, ATA_DRIVE, ATA_D_IBM | ATA_MASTER); + DELAY(10); + ostat0 = ATA_INB(ch->r_io, ATA_STATUS); + if ((ostat0 & 0xf8) != 0xf8 && ostat0 != 0xa5) { + stat0 = ATA_S_BUSY; + mask |= 0x01; + } + ATA_OUTB(ch->r_io, ATA_DRIVE, ATA_D_IBM | ATA_SLAVE); + DELAY(10); + ostat1 = ATA_INB(ch->r_io, ATA_STATUS); + if ((ostat1 & 0xf8) != 0xf8 && ostat1 != 0xa5) { + stat1 = ATA_S_BUSY; + mask |= 0x02; + } + + ch->devices = 0; + if (!mask) + return; + + /* in some setups we dont want to test for a slave */ + if (ch->flags & ATA_NO_SLAVE) { + stat1 = 0x0; + mask &= ~0x02; + } + + if (bootverbose) + ata_printf(ch, -1, "mask=%02x ostat0=%02x ostat2=%02x\n", + mask, ostat0, ostat1); + + /* reset channel */ + ATA_OUTB(ch->r_io, ATA_DRIVE, ATA_D_IBM | ATA_MASTER); + DELAY(10); + ATA_OUTB(ch->r_altio, ATA_ALTSTAT, ATA_A_IDS | ATA_A_RESET); + DELAY(10000); + ATA_OUTB(ch->r_altio, ATA_ALTSTAT, ATA_A_IDS); + DELAY(100000); + ATA_INB(ch->r_io, ATA_ERROR); + + /* wait for BUSY to go inactive */ + for (timeout = 0; timeout < 310000; timeout++) { + if (stat0 & ATA_S_BUSY) { + ATA_OUTB(ch->r_io, ATA_DRIVE, ATA_D_IBM | ATA_MASTER); + DELAY(10); + + /* check for ATAPI signature while its still there */ + lsb = ATA_INB(ch->r_io, ATA_CYL_LSB); + msb = ATA_INB(ch->r_io, ATA_CYL_MSB); + stat0 = ATA_INB(ch->r_io, ATA_STATUS); + if (!(stat0 & ATA_S_BUSY)) { + if (bootverbose) + ata_printf(ch, ATA_MASTER, "ATAPI %02x %02x\n", lsb, msb); + if (lsb == ATAPI_MAGIC_LSB && msb == ATAPI_MAGIC_MSB) + ch->devices |= ATA_ATAPI_MASTER; + } + } + if (stat1 & ATA_S_BUSY) { + ATA_OUTB(ch->r_io, ATA_DRIVE, ATA_D_IBM | ATA_SLAVE); + DELAY(10); + + /* check for ATAPI signature while its still there */ + lsb = ATA_INB(ch->r_io, ATA_CYL_LSB); + msb = ATA_INB(ch->r_io, ATA_CYL_MSB); + stat1 = ATA_INB(ch->r_io, ATA_STATUS); + if (!(stat1 & ATA_S_BUSY)) { + if (bootverbose) + ata_printf(ch, ATA_SLAVE, "ATAPI %02x %02x\n", lsb, msb); + if (lsb == ATAPI_MAGIC_LSB && msb == ATAPI_MAGIC_MSB) + ch->devices |= ATA_ATAPI_SLAVE; + } + } + if (mask == 0x01) /* wait for master only */ + if (!(stat0 & ATA_S_BUSY)) + break; + if (mask == 0x02) /* wait for slave only */ + if (!(stat1 & ATA_S_BUSY)) + break; + if (mask == 0x03) /* wait for both master & slave */ + if (!(stat0 & ATA_S_BUSY) && !(stat1 & ATA_S_BUSY)) + break; + DELAY(100); + } + DELAY(10); + ATA_OUTB(ch->r_altio, ATA_ALTSTAT, ATA_A_4BIT); + + if (stat0 & ATA_S_BUSY) + mask &= ~0x01; + if (stat1 & ATA_S_BUSY) + mask &= ~0x02; + if (bootverbose) + ata_printf(ch, -1, "mask=%02x stat0=%02x stat1=%02x\n", + mask, stat0, stat1); + if (!mask) + return; + + if (mask & 0x01 && ostat0 != 0x00 && !(ch->devices & ATA_ATAPI_MASTER)) { + ATA_OUTB(ch->r_io, ATA_DRIVE, ATA_D_IBM | ATA_MASTER); + DELAY(10); + ATA_OUTB(ch->r_io, ATA_ERROR, 0x58); + ATA_OUTB(ch->r_io, ATA_CYL_LSB, 0xa5); + lsb = ATA_INB(ch->r_io, ATA_ERROR); + msb = ATA_INB(ch->r_io, ATA_CYL_LSB); + if (bootverbose) + ata_printf(ch, ATA_MASTER, "ATA %02x %02x\n", lsb, msb); + if (lsb != 0x58 && msb == 0xa5) + ch->devices |= ATA_ATA_MASTER; + } + if (mask & 0x02 && ostat1 != 0x00 && !(ch->devices & ATA_ATAPI_SLAVE)) { + ATA_OUTB(ch->r_io, ATA_DRIVE, ATA_D_IBM | ATA_SLAVE); + DELAY(10); + ATA_OUTB(ch->r_io, ATA_ERROR, 0x58); + ATA_OUTB(ch->r_io, ATA_CYL_LSB, 0xa5); + lsb = ATA_INB(ch->r_io, ATA_ERROR); + msb = ATA_INB(ch->r_io, ATA_CYL_LSB); + if (bootverbose) + ata_printf(ch, ATA_SLAVE, "ATA %02x %02x\n", lsb, msb); + if (lsb != 0x58 && msb == 0xa5) + ch->devices |= ATA_ATA_SLAVE; + } + if (bootverbose) + ata_printf(ch, -1, "devices=%02x\n", ch->devices); +} + +int +ata_reinit(struct ata_channel *ch) +{ + int devices, misdev, newdev; + + if (!ch->r_io || !ch->r_altio || !ch->r_irq) + return ENXIO; + + ATA_FORCELOCK_CH(ch, ATA_CONTROL); + ch->running = NULL; + devices = ch->devices; + ata_printf(ch, -1, "resetting devices ..\n"); + ata_reset(ch); + + if ((misdev = devices & ~ch->devices)) { +#ifdef DEV_ATADISK + if (misdev & ATA_ATA_MASTER && ch->device[MASTER].driver) + ad_detach(&ch->device[MASTER], 0); + if (misdev & ATA_ATA_SLAVE && ch->device[SLAVE].driver) + ad_detach(&ch->device[SLAVE], 0); +#endif +#if DEV_ATAPIALL + if (misdev & ATA_ATAPI_MASTER && ch->device[MASTER].driver) + atapi_detach(&ch->device[MASTER]); + if (misdev & ATA_ATAPI_SLAVE && ch->device[SLAVE].driver) + atapi_detach(&ch->device[SLAVE]); +#endif + if (misdev & ATA_ATA_MASTER || misdev & ATA_ATAPI_MASTER) { + if (ch->device[MASTER].param) + free(ch->device[MASTER].param, M_ATA); + ch->device[MASTER].param = NULL; + } + if (misdev & ATA_ATA_SLAVE || misdev & ATA_ATAPI_SLAVE) { + if (ch->device[SLAVE].param) + free(ch->device[SLAVE].param, M_ATA); + ch->device[SLAVE].param = NULL; + } + } + if ((newdev = ~devices & ch->devices)) { + if (newdev & ATA_ATA_MASTER) + if (ata_getparam(&ch->device[MASTER], ATA_C_ATA_IDENTIFY)) + newdev &= ~ATA_ATA_MASTER; + if (newdev & ATA_ATA_SLAVE) + if (ata_getparam(&ch->device[SLAVE], ATA_C_ATA_IDENTIFY)) + newdev &= ~ATA_ATA_SLAVE; + if (newdev & ATA_ATAPI_MASTER) + if (ata_getparam(&ch->device[MASTER], ATA_C_ATAPI_IDENTIFY)) + newdev &= ~ATA_ATAPI_MASTER; + if (newdev & ATA_ATAPI_SLAVE) + if (ata_getparam(&ch->device[SLAVE], ATA_C_ATAPI_IDENTIFY)) + newdev &= ~ATA_ATAPI_SLAVE; + } +#ifdef DEV_ATADISK + if (newdev & ATA_ATA_MASTER && !ch->device[MASTER].driver) + ad_attach(&ch->device[MASTER]); + else if (ch->devices & ATA_ATA_MASTER && ch->device[MASTER].driver) { + ata_getparam(&ch->device[MASTER], ATA_C_ATA_IDENTIFY); + ad_reinit(&ch->device[MASTER]); + } + if (newdev & ATA_ATA_SLAVE && !ch->device[SLAVE].driver) + ad_attach(&ch->device[SLAVE]); + else if (ch->devices & (ATA_ATA_SLAVE) && ch->device[SLAVE].driver) { + ata_getparam(&ch->device[SLAVE], ATA_C_ATA_IDENTIFY); + ad_reinit(&ch->device[SLAVE]); + } +#endif +#if DEV_ATAPIALL + if (newdev & ATA_ATAPI_MASTER && !ch->device[MASTER].driver) + atapi_attach(&ch->device[MASTER]); + else if (ch->devices & (ATA_ATAPI_MASTER) && ch->device[MASTER].driver) { + ata_getparam(&ch->device[MASTER], ATA_C_ATAPI_IDENTIFY); + atapi_reinit(&ch->device[MASTER]); + } + if (newdev & ATA_ATAPI_SLAVE && !ch->device[SLAVE].driver) + atapi_attach(&ch->device[SLAVE]); + else if (ch->devices & (ATA_ATAPI_SLAVE) && ch->device[SLAVE].driver) { + ata_getparam(&ch->device[SLAVE], ATA_C_ATAPI_IDENTIFY); + atapi_reinit(&ch->device[SLAVE]); + } +#endif +#ifdef DEV_ATAPICAM + atapi_cam_reinit_bus(ch); +#endif + printf("done\n"); + ATA_UNLOCK_CH(ch); + ata_start(ch); + return 0; +} + +static int +ata_service(struct ata_channel *ch) +{ + /* do we have a SERVICE request from the drive ? */ + if ((ch->status & (ATA_S_SERVICE|ATA_S_ERROR|ATA_S_DRQ)) == ATA_S_SERVICE) { + ATA_OUTB(ch->r_bmio, ATA_BMSTAT_PORT, + ata_dmastatus(ch) | ATA_BMSTAT_INTERRUPT); +#ifdef DEV_ATADISK + if ((ATA_INB(ch->r_io, ATA_DRIVE) & ATA_SLAVE) == ATA_MASTER) { + if ((ch->devices & ATA_ATA_MASTER) && ch->device[MASTER].driver) + return ad_service((struct ad_softc *) + ch->device[MASTER].driver, 0); + } + else { + if ((ch->devices & ATA_ATA_SLAVE) && ch->device[SLAVE].driver) + return ad_service((struct ad_softc *) + ch->device[SLAVE].driver, 0); + } +#endif + } + return ATA_OP_FINISHED; +} + +int +ata_wait(struct ata_device *atadev, u_int8_t mask) +{ + int timeout = 0; + + DELAY(1); + while (timeout < 5000000) { /* timeout 5 secs */ + atadev->channel->status = ATA_INB(atadev->channel->r_io, ATA_STATUS); + + /* if drive fails status, reselect the drive just to be sure */ + if (atadev->channel->status == 0xff) { + ata_prtdev(atadev, "no status, reselecting device\n"); + ATA_OUTB(atadev->channel->r_io, ATA_DRIVE, ATA_D_IBM|atadev->unit); + DELAY(10); + atadev->channel->status = ATA_INB(atadev->channel->r_io,ATA_STATUS); + if (atadev->channel->status == 0xff) + return -1; + } + + /* are we done ? */ + if (!(atadev->channel->status & ATA_S_BUSY)) + break; + + if (timeout > 1000) { + timeout += 1000; + DELAY(1000); + } + else { + timeout += 10; + DELAY(10); + } + } + if (atadev->channel->status & ATA_S_ERROR) + atadev->channel->error = ATA_INB(atadev->channel->r_io, ATA_ERROR); + if (timeout >= 5000000) + return -1; + if (!mask) + return (atadev->channel->status & ATA_S_ERROR); + + /* Wait 50 msec for bits wanted. */ + timeout = 5000; + while (timeout--) { + atadev->channel->status = ATA_INB(atadev->channel->r_io, ATA_STATUS); + if ((atadev->channel->status & mask) == mask) { + if (atadev->channel->status & ATA_S_ERROR) + atadev->channel->error=ATA_INB(atadev->channel->r_io,ATA_ERROR); + return (atadev->channel->status & ATA_S_ERROR); + } + DELAY (10); + } + return -1; +} + +int +ata_command(struct ata_device *atadev, u_int8_t command, + u_int64_t lba, u_int16_t count, u_int16_t feature, int flags) +{ + int error = 0; +#ifdef ATA_DEBUG + ata_prtdev(atadev, "ata_command: addr=%04lx, cmd=%02x, " + "lba=%jd, count=%d, feature=%d, flags=%02x\n", + rman_get_start(atadev->channel->r_io), + command, (intmax_t)lba, count, feature, flags); +#endif + + /* select device */ + ATA_OUTB(atadev->channel->r_io, ATA_DRIVE, ATA_D_IBM | atadev->unit); + + /* disable interrupt from device */ + if (atadev->channel->flags & ATA_QUEUED) + ATA_OUTB(atadev->channel->r_altio, ATA_ALTSTAT, ATA_A_IDS | ATA_A_4BIT); + + /* ready to issue command ? */ + if (ata_wait(atadev, 0) < 0) { + ata_prtdev(atadev, "timeout sending command=%02x s=%02x e=%02x\n", + command, atadev->channel->status, atadev->channel->error); + return -1; + } + + /* only use 48bit addressing if needed because of the overhead */ + if ((lba > 268435455 || count > 256) && atadev->param && + atadev->param->support.address48) { + ATA_OUTB(atadev->channel->r_io, ATA_FEATURE, (feature>>8) & 0xff); + ATA_OUTB(atadev->channel->r_io, ATA_FEATURE, feature); + ATA_OUTB(atadev->channel->r_io, ATA_COUNT, (count>>8) & 0xff); + ATA_OUTB(atadev->channel->r_io, ATA_COUNT, count & 0xff); + ATA_OUTB(atadev->channel->r_io, ATA_SECTOR, (lba>>24) & 0xff); + ATA_OUTB(atadev->channel->r_io, ATA_SECTOR, lba & 0xff); + ATA_OUTB(atadev->channel->r_io, ATA_CYL_LSB, (lba>>32) & 0xff); + ATA_OUTB(atadev->channel->r_io, ATA_CYL_LSB, (lba>>8) & 0xff); + ATA_OUTB(atadev->channel->r_io, ATA_CYL_MSB, (lba>>40) & 0xff); + ATA_OUTB(atadev->channel->r_io, ATA_CYL_MSB, (lba>>16) & 0xff); + ATA_OUTB(atadev->channel->r_io, ATA_DRIVE, ATA_D_LBA | atadev->unit); + + /* translate command into 48bit version */ + switch (command) { + case ATA_C_READ: + command = ATA_C_READ48; break; + case ATA_C_READ_MUL: + command = ATA_C_READ_MUL48; break; + case ATA_C_READ_DMA: + command = ATA_C_READ_DMA48; break; + case ATA_C_READ_DMA_QUEUED: + command = ATA_C_READ_DMA_QUEUED48; break; + case ATA_C_WRITE: + command = ATA_C_WRITE48; break; + case ATA_C_WRITE_MUL: + command = ATA_C_WRITE_MUL48; break; + case ATA_C_WRITE_DMA: + command = ATA_C_WRITE_DMA48; break; + case ATA_C_WRITE_DMA_QUEUED: + command = ATA_C_WRITE_DMA_QUEUED48; break; + case ATA_C_FLUSHCACHE: + command = ATA_C_FLUSHCACHE48; break; + default: + ata_prtdev(atadev, "can't translate cmd to 48bit version\n"); + return -1; + } + } + else { + ATA_OUTB(atadev->channel->r_io, ATA_FEATURE, feature); + ATA_OUTB(atadev->channel->r_io, ATA_COUNT, count); + ATA_OUTB(atadev->channel->r_io, ATA_SECTOR, lba & 0xff); + ATA_OUTB(atadev->channel->r_io, ATA_CYL_LSB, (lba>>8) & 0xff); + ATA_OUTB(atadev->channel->r_io, ATA_CYL_MSB, (lba>>16) & 0xff); + if (atadev->flags & ATA_D_USE_CHS) + ATA_OUTB(atadev->channel->r_io, ATA_DRIVE, + ATA_D_IBM | atadev->unit | ((lba>>24) & 0xf)); + else + ATA_OUTB(atadev->channel->r_io, ATA_DRIVE, + ATA_D_IBM | ATA_D_LBA | atadev->unit | ((lba>>24) &0xf)); + } + + switch (flags & ATA_WAIT_MASK) { + case ATA_IMMEDIATE: + ATA_OUTB(atadev->channel->r_io, ATA_CMD, command); + + /* enable interrupt */ + if (atadev->channel->flags & ATA_QUEUED) + ATA_OUTB(atadev->channel->r_altio, ATA_ALTSTAT, ATA_A_4BIT); + break; + + case ATA_WAIT_INTR: + atadev->channel->active |= ATA_WAIT_INTR; + ATA_OUTB(atadev->channel->r_io, ATA_CMD, command); + + /* enable interrupt */ + if (atadev->channel->flags & ATA_QUEUED) + ATA_OUTB(atadev->channel->r_altio, ATA_ALTSTAT, ATA_A_4BIT); + + if (tsleep((caddr_t)atadev->channel, PRIBIO, "atacmd", 10 * hz)) { + ata_prtdev(atadev, "timeout waiting for interrupt\n"); + atadev->channel->active &= ~ATA_WAIT_INTR; + error = -1; + } + break; + + case ATA_WAIT_READY: + atadev->channel->active |= ATA_WAIT_READY; + ATA_OUTB(atadev->channel->r_io, ATA_CMD, command); + if (ata_wait(atadev, ATA_S_READY) < 0) { + ata_prtdev(atadev, "timeout waiting for cmd=%02x s=%02x e=%02x\n", + command, atadev->channel->status,atadev->channel->error); + error = -1; + } + atadev->channel->active &= ~ATA_WAIT_READY; + break; + } + return error; +} + +static void +ata_drawer_start(struct ata_device *atadev) +{ + ATA_INB(atadev->channel->r_io, ATA_DRIVE); + DELAY(1); + ATA_OUTB(atadev->channel->r_io, ATA_DRIVE, ATA_D_IBM | atadev->unit); + DELAY(1); + ATA_OUTB(atadev->channel->r_io, ATA_DRIVE, ATA_D_IBM | atadev->unit); + DELAY(1); + ATA_OUTB(atadev->channel->r_io, ATA_DRIVE, ATA_D_IBM | atadev->unit); + DELAY(1); + ATA_INB(atadev->channel->r_io, ATA_COUNT); + DELAY(1); + ATA_INB(atadev->channel->r_io, ATA_DRIVE); + DELAY(1); +} + +static void +ata_drawer_end(struct ata_device *atadev) +{ + ATA_OUTB(atadev->channel->r_io, ATA_DRIVE, ATA_D_IBM | atadev->unit); + DELAY(1); +} + +static void +ata_chip_start(struct ata_device *atadev) +{ + ATA_OUTB(atadev->channel->r_io, ATA_SECTOR, 0x0b); + ATA_OUTB(atadev->channel->r_io, ATA_SECTOR, 0x0a); + DELAY(25); + ATA_OUTB(atadev->channel->r_io, ATA_SECTOR, 0x08); +} + +static void +ata_chip_end(struct ata_device *atadev) +{ + ATA_OUTB(atadev->channel->r_io, ATA_SECTOR, 0x08); + DELAY(64); + ATA_OUTB(atadev->channel->r_io, ATA_SECTOR, 0x0a); + DELAY(25); + ATA_OUTB(atadev->channel->r_io, ATA_SECTOR, 0x0b); + DELAY(64); +} + +static u_int8_t +ata_chip_rdbit(struct ata_device *atadev) +{ + u_int8_t val; + + ATA_OUTB(atadev->channel->r_io, ATA_SECTOR, 0); + DELAY(64); + ATA_OUTB(atadev->channel->r_io, ATA_SECTOR, 0x02); + DELAY(25); + val = ATA_INB(atadev->channel->r_io, ATA_SECTOR) & 0x01; + DELAY(38); + return val; +} + +static void +ata_chip_wrbit(struct ata_device *atadev, u_int8_t data) +{ + ATA_OUTB(atadev->channel->r_io, ATA_SECTOR, 0x08 | (data & 0x01)); + DELAY(64); + ATA_OUTB(atadev->channel->r_io, ATA_SECTOR, 0x08 | 0x02 | (data & 0x01)); + DELAY(64); +} + +static u_int8_t +ata_chip_rw(struct ata_device *atadev, int rw, u_int8_t val) +{ + int i; + + if (rw) { + for (i = 0; i < 8; i++) + ata_chip_wrbit(atadev, (val & (0x80 >> i)) ? 1 : 0); + } + else { + for (i = 0; i < 8; i++) + val = (val << 1) | ata_chip_rdbit(atadev); + } + ata_chip_wrbit(atadev, 0); + return val; +} + +static u_int8_t +ata_drawersensor(struct ata_device *atadev, int rw, u_int8_t idx, u_int8_t data) +{ + ata_drawer_start(atadev); + ata_chip_start(atadev); + ata_chip_rw(atadev, 1, 0x5a); + ata_chip_rw(atadev, 1, idx); + if (rw) { + ata_chip_rw(atadev, 1, data); + } + else { + ata_chip_end(atadev); + ata_chip_start(atadev); + ata_chip_rw(atadev, 1, 0x5b); + data = ata_chip_rw(atadev, 0, 0); + } + ata_chip_end(atadev); + ata_drawer_end(atadev); + return data; +} + +void +ata_drawerleds(struct ata_device *atadev, u_int8_t color) +{ + ata_drawer_start(atadev); + ATA_OUTB(atadev->channel->r_io, ATA_COUNT, color); + DELAY(1); + ata_drawer_end(atadev); +} + +static void +ata_change_mode(struct ata_device *atadev, int mode) +{ + int umode, wmode, pmode; + + umode = ata_umode(atadev->param); + wmode = ata_wmode(atadev->param); + pmode = ata_pmode(atadev->param); + + switch (mode & ATA_DMA_MASK) { + case ATA_UDMA: + if ((mode & ATA_MODE_MASK) < umode) + umode = mode & ATA_MODE_MASK; + break; + case ATA_WDMA: + if ((mode & ATA_MODE_MASK) < wmode) + wmode = mode & ATA_MODE_MASK; + umode = -1; + break; + default: + if (((mode & ATA_MODE_MASK) - ATA_PIO0) < pmode) + pmode = (mode & ATA_MODE_MASK) - ATA_PIO0; + umode = -1; + wmode = -1; + } + + ATA_SLEEPLOCK_CH(atadev->channel, ATA_ACTIVE); + ata_dmainit(atadev, pmode, wmode, umode); + ATA_UNLOCK_CH(atadev->channel); + ata_start(atadev->channel); /* XXX SOS */ +} + +int +ata_printf(struct ata_channel *ch, int device, const char * fmt, ...) +{ + va_list ap; + int ret; + + if (device == -1) + ret = printf("ata%d: ", device_get_unit(ch->dev)); + else { + if (ch->device[ATA_DEV(device)].name) + ret = printf("%s: ", ch->device[ATA_DEV(device)].name); + else + ret = printf("ata%d-%s: ", device_get_unit(ch->dev), + (device == ATA_MASTER) ? "master" : "slave"); + } + va_start(ap, fmt); + ret += vprintf(fmt, ap); + va_end(ap); + return ret; +} + +int +ata_prtdev(struct ata_device *atadev, const char * fmt, ...) +{ + va_list ap; + int ret; + + if (atadev->name) + ret = printf("%s: ", atadev->name); + else + ret = printf("ata%d-%s: ", device_get_unit(atadev->channel->dev), + (atadev->unit == ATA_MASTER) ? "master" : "slave"); + va_start(ap, fmt); + ret += vprintf(fmt, ap); + va_end(ap); + return ret; +} + +void +ata_set_name(struct ata_device *atadev, char *name, int lun) +{ + atadev->name = malloc(strlen(name) + 4, M_ATA, M_NOWAIT); + if (atadev->name) + sprintf(atadev->name, "%s%d", name, lun); +} + +void +ata_free_name(struct ata_device *atadev) +{ + if (atadev->name) + free(atadev->name, M_ATA); + atadev->name = NULL; +} + +int +ata_get_lun(u_int32_t *map) +{ + int lun = ffs(~*map) - 1; + + *map |= (1 << lun); + return lun; +} + +int +ata_test_lun(u_int32_t *map, int lun) +{ + return (*map & (1 << lun)); +} + +void +ata_free_lun(u_int32_t *map, int lun) +{ + *map &= ~(1 << lun); +} + +char * +ata_mode2str(int mode) +{ + switch (mode) { + case ATA_PIO: return "BIOSPIO"; + case ATA_PIO0: return "PIO0"; + case ATA_PIO1: return "PIO1"; + case ATA_PIO2: return "PIO2"; + case ATA_PIO3: return "PIO3"; + case ATA_PIO4: return "PIO4"; + case ATA_DMA: return "BIOSDMA"; + case ATA_WDMA2: return "WDMA2"; + case ATA_UDMA2: return "UDMA33"; + case ATA_UDMA4: return "UDMA66"; + case ATA_UDMA5: return "UDMA100"; + case ATA_UDMA6: return "UDMA133"; + default: return "???"; + } +} + +int +ata_pmode(struct ata_params *ap) +{ + if (ap->atavalid & ATA_FLAG_64_70) { + if (ap->apiomodes & 2) + return 4; + if (ap->apiomodes & 1) + return 3; + } + if (ap->retired_piomode == 2) + return 2; + if (ap->retired_piomode == 1) + return 1; + if (ap->retired_piomode == 0) + return 0; + return -1; +} + +int +ata_wmode(struct ata_params *ap) +{ + if (ap->mwdmamodes & 0x04) + return 2; + if (ap->mwdmamodes & 0x02) + return 1; + if (ap->mwdmamodes & 0x01) + return 0; + return -1; +} + +int +ata_umode(struct ata_params *ap) +{ + if (ap->atavalid & ATA_FLAG_88) { + if (ap->udmamodes & 0x40) + return 6; + if (ap->udmamodes & 0x20) + return 5; + if (ap->udmamodes & 0x10) + return 4; + if (ap->udmamodes & 0x08) + return 3; + if (ap->udmamodes & 0x04) + return 2; + if (ap->udmamodes & 0x02) + return 1; + if (ap->udmamodes & 0x01) + return 0; + } + return -1; +} + +static void +bswap(int8_t *buf, int len) +{ + u_int16_t *ptr = (u_int16_t*)(buf + len); + + while (--ptr >= (u_int16_t*)buf) + *ptr = ntohs(*ptr); +} + +static void +btrim(int8_t *buf, int len) +{ + int8_t *ptr; + + for (ptr = buf; ptr < buf+len; ++ptr) + if (!*ptr) + *ptr = ' '; + for (ptr = buf + len - 1; ptr >= buf && *ptr == ' '; --ptr) + *ptr = 0; +} + +static void +bpack(int8_t *src, int8_t *dst, int len) +{ + int i, j, blank; + + for (i = j = blank = 0 ; i < len; i++) { + if (blank && src[i] == ' ') continue; + if (blank && src[i] != ' ') { + dst[j++] = src[i]; + blank = 0; + continue; + } + if (src[i] == ' ') { + blank = 1; + if (i == 0) + continue; + } + dst[j++] = src[i]; + } + if (j < len) + dst[j] = 0x00; +} + +static void +ata_init(void) +{ + /* register controlling device */ + make_dev(&ata_cdevsw, 0, UID_ROOT, GID_OPERATOR, 0600, "ata"); + + /* register boot attach to be run when interrupts are enabled */ + if (!(ata_delayed_attach = (struct intr_config_hook *) + malloc(sizeof(struct intr_config_hook), + M_TEMP, M_NOWAIT | M_ZERO))) { + printf("ata: malloc of delayed attach hook failed\n"); + return; + } + + ata_delayed_attach->ich_func = (void*)ata_boot_attach; + if (config_intrhook_establish(ata_delayed_attach) != 0) { + printf("ata: config_intrhook_establish failed\n"); + free(ata_delayed_attach, M_TEMP); + } +} +SYSINIT(atadev, SI_SUB_DRIVERS, SI_ORDER_SECOND, ata_init, NULL) |