2009-04-04 01:31:08 +04:00
|
|
|
/* $NetBSD: ata.c,v 1.104 2009/04/03 21:31:08 dyoung Exp $ */
|
2003-12-14 05:45:48 +03:00
|
|
|
|
1998-10-12 20:09:10 +04:00
|
|
|
/*
|
2001-12-03 03:20:22 +03:00
|
|
|
* Copyright (c) 1998, 2001 Manuel Bouyer. All rights reserved.
|
1998-10-12 20:09:10 +04:00
|
|
|
*
|
|
|
|
* 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 Manuel Bouyer.
|
|
|
|
* 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.
|
2005-02-27 03:26:58 +03:00
|
|
|
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
1998-10-12 20:09:10 +04:00
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
|
2001-11-13 15:51:12 +03:00
|
|
|
#include <sys/cdefs.h>
|
2009-04-04 01:31:08 +04:00
|
|
|
__KERNEL_RCSID(0, "$NetBSD: ata.c,v 1.104 2009/04/03 21:31:08 dyoung Exp $");
|
2001-11-13 15:51:12 +03:00
|
|
|
|
2007-03-17 09:41:35 +03:00
|
|
|
#include "opt_ata.h"
|
1998-10-12 20:09:10 +04:00
|
|
|
|
|
|
|
#include <sys/param.h>
|
|
|
|
#include <sys/systm.h>
|
|
|
|
#include <sys/kernel.h>
|
|
|
|
#include <sys/malloc.h>
|
|
|
|
#include <sys/device.h>
|
2004-08-02 01:40:41 +04:00
|
|
|
#include <sys/conf.h>
|
|
|
|
#include <sys/fcntl.h>
|
2003-12-30 19:28:37 +03:00
|
|
|
#include <sys/proc.h>
|
2004-08-13 01:34:52 +04:00
|
|
|
#include <sys/pool.h>
|
2003-12-30 19:28:37 +03:00
|
|
|
#include <sys/kthread.h>
|
|
|
|
#include <sys/errno.h>
|
2004-08-02 01:40:41 +04:00
|
|
|
#include <sys/ataio.h>
|
2007-12-20 23:12:01 +03:00
|
|
|
#include <sys/kmem.h>
|
2008-01-05 00:17:40 +03:00
|
|
|
#include <sys/simplelock.h>
|
2007-10-19 15:59:34 +04:00
|
|
|
#include <sys/intr.h>
|
|
|
|
#include <sys/bus.h>
|
2001-12-03 03:11:15 +03:00
|
|
|
|
2006-09-07 16:34:41 +04:00
|
|
|
#include <dev/ata/ataconf.h>
|
1998-10-12 20:09:10 +04:00
|
|
|
#include <dev/ata/atareg.h>
|
|
|
|
#include <dev/ata/atavar.h>
|
2006-09-07 16:34:41 +04:00
|
|
|
#include <dev/ic/wdcvar.h> /* for PIOBM */
|
1998-10-12 20:09:10 +04:00
|
|
|
|
2003-12-30 19:28:37 +03:00
|
|
|
#include "locators.h"
|
|
|
|
|
2004-08-21 02:02:40 +04:00
|
|
|
#include "atapibus.h"
|
|
|
|
#include "ataraid.h"
|
|
|
|
|
2005-02-27 03:26:58 +03:00
|
|
|
#if NATARAID > 0
|
|
|
|
#include <dev/ata/ata_raidvar.h>
|
2004-08-21 02:02:40 +04:00
|
|
|
#endif
|
|
|
|
|
1998-10-12 20:09:10 +04:00
|
|
|
#define DEBUG_FUNCS 0x08
|
|
|
|
#define DEBUG_PROBE 0x10
|
2003-12-30 19:28:37 +03:00
|
|
|
#define DEBUG_DETACH 0x20
|
2004-08-13 06:10:43 +04:00
|
|
|
#define DEBUG_XFERS 0x40
|
2004-08-13 08:10:49 +04:00
|
|
|
#ifdef ATADEBUG
|
|
|
|
int atadebug_mask = 0;
|
|
|
|
#define ATADEBUG_PRINT(args, level) \
|
2006-10-15 04:00:00 +04:00
|
|
|
if (atadebug_mask & (level)) \
|
1998-10-12 20:09:10 +04:00
|
|
|
printf args
|
|
|
|
#else
|
2004-08-13 08:10:49 +04:00
|
|
|
#define ATADEBUG_PRINT(args, level)
|
1998-10-12 20:09:10 +04:00
|
|
|
#endif
|
|
|
|
|
2007-03-12 21:18:22 +03:00
|
|
|
POOL_INIT(ata_xfer_pool, sizeof(struct ata_xfer), 0, 0, 0, "ataspl", NULL,
|
|
|
|
IPL_BIO);
|
2004-08-13 01:34:52 +04:00
|
|
|
|
2004-08-21 02:02:40 +04:00
|
|
|
/*
|
|
|
|
* A queue of atabus instances, used to ensure the same bus probe order
|
|
|
|
* for a given hardware configuration at each boot.
|
|
|
|
*/
|
|
|
|
struct atabus_initq_head atabus_initq_head =
|
|
|
|
TAILQ_HEAD_INITIALIZER(atabus_initq_head);
|
|
|
|
struct simplelock atabus_interlock = SIMPLELOCK_INITIALIZER;
|
|
|
|
|
2008-10-01 22:23:55 +04:00
|
|
|
/* kernel thread probing devices on a atabus. Only one probing at once */
|
|
|
|
struct lwp *atabus_configlwp;
|
|
|
|
|
2003-12-30 19:28:37 +03:00
|
|
|
/*****************************************************************************
|
|
|
|
* ATA bus layer.
|
|
|
|
*
|
|
|
|
* ATA controllers attach an atabus instance, which handles probing the bus
|
|
|
|
* for drives, etc.
|
|
|
|
*****************************************************************************/
|
|
|
|
|
2004-08-02 01:40:41 +04:00
|
|
|
dev_type_open(atabusopen);
|
|
|
|
dev_type_close(atabusclose);
|
|
|
|
dev_type_ioctl(atabusioctl);
|
|
|
|
|
|
|
|
const struct cdevsw atabus_cdevsw = {
|
|
|
|
atabusopen, atabusclose, noread, nowrite, atabusioctl,
|
2006-08-28 03:50:53 +04:00
|
|
|
nostop, notty, nopoll, nommap, nokqfilter, D_OTHER
|
2004-08-02 01:40:41 +04:00
|
|
|
};
|
|
|
|
|
|
|
|
extern struct cfdriver atabus_cd;
|
|
|
|
|
2008-01-10 10:41:20 +03:00
|
|
|
static void atabus_childdetached(device_t, device_t);
|
2008-02-29 09:38:28 +03:00
|
|
|
static bool atabus_resume(device_t PMF_FN_PROTO);
|
|
|
|
static bool atabus_suspend(device_t PMF_FN_PROTO);
|
2008-10-01 22:23:55 +04:00
|
|
|
static void atabusconfig_thread(void *);
|
2004-08-02 01:40:41 +04:00
|
|
|
|
2003-12-30 19:28:37 +03:00
|
|
|
/*
|
|
|
|
* atabusprint:
|
|
|
|
*
|
|
|
|
* Autoconfiguration print routine used by ATA controllers when
|
|
|
|
* attaching an atabus instance.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
atabusprint(void *aux, const char *pnp)
|
|
|
|
{
|
2004-08-14 19:08:04 +04:00
|
|
|
struct ata_channel *chan = aux;
|
2005-02-27 03:26:58 +03:00
|
|
|
|
2003-12-30 19:28:37 +03:00
|
|
|
if (pnp)
|
|
|
|
aprint_normal("atabus at %s", pnp);
|
2004-01-04 01:56:52 +03:00
|
|
|
aprint_normal(" channel %d", chan->ch_channel);
|
2003-12-30 19:28:37 +03:00
|
|
|
|
|
|
|
return (UNCONF);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* ataprint:
|
|
|
|
*
|
|
|
|
* Autoconfiguration print routine.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
ataprint(void *aux, const char *pnp)
|
|
|
|
{
|
|
|
|
struct ata_device *adev = aux;
|
|
|
|
|
|
|
|
if (pnp)
|
|
|
|
aprint_normal("wd at %s", pnp);
|
|
|
|
aprint_normal(" drive %d", adev->adev_drv_data->drive);
|
|
|
|
|
|
|
|
return (UNCONF);
|
|
|
|
}
|
|
|
|
|
2004-08-21 02:17:06 +04:00
|
|
|
/*
|
|
|
|
* ata_channel_attach:
|
|
|
|
*
|
|
|
|
* Common parts of attaching an atabus to an ATA controller channel.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
ata_channel_attach(struct ata_channel *chp)
|
|
|
|
{
|
|
|
|
|
|
|
|
if (chp->ch_flags & ATACH_DISABLED)
|
|
|
|
return;
|
2005-02-27 03:26:58 +03:00
|
|
|
|
2007-07-10 00:51:58 +04:00
|
|
|
callout_init(&chp->ch_callout, 0);
|
2004-08-21 02:17:06 +04:00
|
|
|
|
|
|
|
TAILQ_INIT(&chp->ch_queue->queue_xfer);
|
|
|
|
chp->ch_queue->queue_freeze = 0;
|
2005-05-17 01:43:33 +04:00
|
|
|
chp->ch_queue->queue_flags = 0;
|
2004-08-21 02:17:06 +04:00
|
|
|
chp->ch_queue->active_xfer = NULL;
|
|
|
|
|
2008-03-18 23:46:35 +03:00
|
|
|
chp->atabus = config_found_ia(chp->ch_atac->atac_dev, "ata", chp,
|
2005-07-07 21:51:31 +04:00
|
|
|
atabusprint);
|
2004-08-21 02:17:06 +04:00
|
|
|
}
|
|
|
|
|
2004-08-21 02:02:40 +04:00
|
|
|
static void
|
|
|
|
atabusconfig(struct atabus_softc *atabus_sc)
|
|
|
|
{
|
|
|
|
struct ata_channel *chp = atabus_sc->sc_chan;
|
|
|
|
struct atac_softc *atac = chp->ch_atac;
|
|
|
|
struct atabus_initq *atabus_initq = NULL;
|
2008-11-16 22:31:21 +03:00
|
|
|
int i, s, error;
|
|
|
|
|
|
|
|
/* we are in the atabus's thread context */
|
|
|
|
s = splbio();
|
|
|
|
chp->ch_flags |= ATACH_TH_RUN;
|
|
|
|
splx(s);
|
2004-08-21 02:02:40 +04:00
|
|
|
|
|
|
|
/* Probe for the drives. */
|
2006-10-25 21:33:02 +04:00
|
|
|
/* XXX for SATA devices we will power up all drives at once */
|
2004-08-21 02:02:40 +04:00
|
|
|
(*atac->atac_probe)(chp);
|
|
|
|
|
|
|
|
ATADEBUG_PRINT(("atabusattach: ch_drive_flags 0x%x 0x%x\n",
|
|
|
|
chp->ch_drive[0].drive_flags, chp->ch_drive[1].drive_flags),
|
|
|
|
DEBUG_PROBE);
|
|
|
|
|
2008-11-16 22:31:21 +03:00
|
|
|
/* next operations will occurs in a separate thread */
|
|
|
|
s = splbio();
|
|
|
|
chp->ch_flags &= ~ATACH_TH_RUN;
|
|
|
|
splx(s);
|
|
|
|
|
2008-10-01 22:23:55 +04:00
|
|
|
/* Make sure the devices probe in atabus order to avoid jitter. */
|
|
|
|
simple_lock(&atabus_interlock);
|
|
|
|
while(1) {
|
|
|
|
atabus_initq = TAILQ_FIRST(&atabus_initq_head);
|
|
|
|
if (atabus_initq->atabus_sc == atabus_sc)
|
|
|
|
break;
|
|
|
|
ltsleep(&atabus_initq_head, PRIBIO, "ata_initq", 0,
|
|
|
|
&atabus_interlock);
|
|
|
|
}
|
|
|
|
simple_unlock(&atabus_interlock);
|
|
|
|
|
2004-08-21 02:02:40 +04:00
|
|
|
/* If no drives, abort here */
|
|
|
|
for (i = 0; i < chp->ch_ndrive; i++)
|
|
|
|
if ((chp->ch_drive[i].drive_flags & DRIVE) != 0)
|
|
|
|
break;
|
|
|
|
if (i == chp->ch_ndrive)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
/* Shortcut in case we've been shutdown */
|
|
|
|
if (chp->ch_flags & ATACH_SHUTDOWN)
|
|
|
|
goto out;
|
|
|
|
|
2008-10-01 22:23:55 +04:00
|
|
|
|
|
|
|
if ((error = kthread_create(PRI_NONE, 0, NULL, atabusconfig_thread,
|
|
|
|
atabus_sc, &atabus_configlwp,
|
|
|
|
"%scnf", device_xname(atac->atac_dev))) != 0)
|
|
|
|
aprint_error_dev(atac->atac_dev,
|
|
|
|
"unable to create config thread: error %d\n", error);
|
|
|
|
return;
|
|
|
|
|
|
|
|
out:
|
2004-08-21 02:02:40 +04:00
|
|
|
simple_lock(&atabus_interlock);
|
2008-10-01 22:23:55 +04:00
|
|
|
TAILQ_REMOVE(&atabus_initq_head, atabus_initq, atabus_initq);
|
2004-08-21 02:02:40 +04:00
|
|
|
simple_unlock(&atabus_interlock);
|
|
|
|
|
2008-10-01 22:23:55 +04:00
|
|
|
free(atabus_initq, M_DEVBUF);
|
|
|
|
wakeup(&atabus_initq_head);
|
|
|
|
|
|
|
|
ata_delref(chp);
|
|
|
|
|
|
|
|
config_pending_decr();
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* atabus_configthread: finish attach of atabus's childrens, in a separate
|
|
|
|
* kernel thread.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
atabusconfig_thread(void *arg)
|
|
|
|
{
|
|
|
|
struct atabus_softc *atabus_sc = arg;
|
|
|
|
struct ata_channel *chp = atabus_sc->sc_chan;
|
|
|
|
struct atac_softc *atac = chp->ch_atac;
|
|
|
|
int i, s;
|
|
|
|
struct atabus_initq *atabus_initq = NULL;
|
|
|
|
|
|
|
|
simple_lock(&atabus_interlock);
|
|
|
|
atabus_initq = TAILQ_FIRST(&atabus_initq_head);
|
|
|
|
simple_unlock(&atabus_interlock);
|
|
|
|
KASSERT(atabus_initq->atabus_sc == atabus_sc);
|
2004-08-21 02:02:40 +04:00
|
|
|
/*
|
|
|
|
* Attach an ATAPI bus, if needed.
|
|
|
|
*/
|
|
|
|
for (i = 0; i < chp->ch_ndrive; i++) {
|
|
|
|
if (chp->ch_drive[i].drive_flags & DRIVE_ATAPI) {
|
|
|
|
#if NATAPIBUS > 0
|
|
|
|
(*atac->atac_atapibus_attach)(atabus_sc);
|
|
|
|
#else
|
|
|
|
/*
|
|
|
|
* Fake the autoconfig "not configured" message
|
|
|
|
*/
|
|
|
|
aprint_normal("atapibus at %s not configured\n",
|
2008-03-18 23:46:35 +03:00
|
|
|
device_xname(atac->atac_dev));
|
2004-08-21 02:02:40 +04:00
|
|
|
chp->atapibus = NULL;
|
2004-08-21 04:28:34 +04:00
|
|
|
s = splbio();
|
2004-08-21 02:02:40 +04:00
|
|
|
for (i = 0; i < chp->ch_ndrive; i++)
|
|
|
|
chp->ch_drive[i].drive_flags &= ~DRIVE_ATAPI;
|
2004-08-21 04:28:34 +04:00
|
|
|
splx(s);
|
2004-08-21 02:02:40 +04:00
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < chp->ch_ndrive; i++) {
|
|
|
|
struct ata_device adev;
|
|
|
|
if ((chp->ch_drive[i].drive_flags &
|
|
|
|
(DRIVE_ATA | DRIVE_OLD)) == 0) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
memset(&adev, 0, sizeof(struct ata_device));
|
|
|
|
adev.adev_bustype = atac->atac_bustype_ata;
|
|
|
|
adev.adev_channel = chp->ch_channel;
|
|
|
|
adev.adev_openings = 1;
|
|
|
|
adev.adev_drv_data = &chp->ch_drive[i];
|
2008-03-18 23:46:35 +03:00
|
|
|
chp->ata_drives[i] = config_found_ia(atabus_sc->sc_dev,
|
2005-07-07 21:51:31 +04:00
|
|
|
"ata_hl", &adev, ataprint);
|
2004-08-21 02:02:40 +04:00
|
|
|
if (chp->ata_drives[i] != NULL)
|
|
|
|
ata_probe_caps(&chp->ch_drive[i]);
|
2004-08-21 04:28:34 +04:00
|
|
|
else {
|
|
|
|
s = splbio();
|
2004-08-21 02:02:40 +04:00
|
|
|
chp->ch_drive[i].drive_flags &=
|
|
|
|
~(DRIVE_ATA | DRIVE_OLD);
|
2004-08-21 04:28:34 +04:00
|
|
|
splx(s);
|
|
|
|
}
|
2004-08-21 02:02:40 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* now that we know the drives, the controller can set its modes */
|
|
|
|
if (atac->atac_set_modes) {
|
|
|
|
(*atac->atac_set_modes)(chp);
|
|
|
|
ata_print_modes(chp);
|
|
|
|
}
|
|
|
|
#if NATARAID > 0
|
|
|
|
if (atac->atac_cap & ATAC_CAP_RAID)
|
|
|
|
for (i = 0; i < chp->ch_ndrive; i++)
|
|
|
|
if (chp->ata_drives[i] != NULL)
|
|
|
|
ata_raid_check_component(chp->ata_drives[i]);
|
|
|
|
#endif /* NATARAID > 0 */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* reset drive_flags for unattached devices, reset state for attached
|
|
|
|
* ones
|
|
|
|
*/
|
2004-08-21 04:28:34 +04:00
|
|
|
s = splbio();
|
2004-08-21 02:02:40 +04:00
|
|
|
for (i = 0; i < chp->ch_ndrive; i++) {
|
|
|
|
if (chp->ch_drive[i].drv_softc == NULL)
|
|
|
|
chp->ch_drive[i].drive_flags = 0;
|
|
|
|
else
|
|
|
|
chp->ch_drive[i].state = 0;
|
|
|
|
}
|
2004-08-21 04:28:34 +04:00
|
|
|
splx(s);
|
2004-08-21 02:02:40 +04:00
|
|
|
|
2006-10-15 04:00:00 +04:00
|
|
|
simple_lock(&atabus_interlock);
|
|
|
|
TAILQ_REMOVE(&atabus_initq_head, atabus_initq, atabus_initq);
|
|
|
|
simple_unlock(&atabus_interlock);
|
2004-08-21 02:02:40 +04:00
|
|
|
|
2006-10-15 04:00:00 +04:00
|
|
|
free(atabus_initq, M_DEVBUF);
|
|
|
|
wakeup(&atabus_initq_head);
|
2004-08-21 02:02:40 +04:00
|
|
|
|
|
|
|
ata_delref(chp);
|
|
|
|
|
|
|
|
config_pending_decr();
|
2008-10-01 22:23:55 +04:00
|
|
|
kthread_exit(0);
|
2004-08-21 02:02:40 +04:00
|
|
|
}
|
|
|
|
|
2003-12-30 19:28:37 +03:00
|
|
|
/*
|
|
|
|
* atabus_thread:
|
|
|
|
*
|
|
|
|
* Worker thread for the ATA bus.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
atabus_thread(void *arg)
|
|
|
|
{
|
|
|
|
struct atabus_softc *sc = arg;
|
2004-08-14 19:08:04 +04:00
|
|
|
struct ata_channel *chp = sc->sc_chan;
|
2004-01-01 20:18:53 +03:00
|
|
|
struct ata_xfer *xfer;
|
2004-08-14 19:08:04 +04:00
|
|
|
int i, s;
|
2003-12-30 19:28:37 +03:00
|
|
|
|
|
|
|
s = splbio();
|
2008-11-16 22:31:21 +03:00
|
|
|
chp->ch_flags |= ATACH_TH_RUN;
|
|
|
|
|
2004-08-14 19:08:04 +04:00
|
|
|
/*
|
|
|
|
* Probe the drives. Reset all flags to 0 to indicate to controllers
|
|
|
|
* that can re-probe that all drives must be probed..
|
|
|
|
*
|
|
|
|
* Note: ch_ndrive may be changed during the probe.
|
|
|
|
*/
|
|
|
|
for (i = 0; i < ATA_MAXDRIVES; i++)
|
|
|
|
chp->ch_drive[i].drive_flags = 0;
|
2003-12-30 19:28:37 +03:00
|
|
|
splx(s);
|
|
|
|
|
2005-05-17 01:43:33 +04:00
|
|
|
atabusconfig(sc);
|
2003-12-30 19:28:37 +03:00
|
|
|
|
2005-05-20 18:39:04 +04:00
|
|
|
s = splbio();
|
2003-12-30 19:28:37 +03:00
|
|
|
for (;;) {
|
2004-08-14 19:08:04 +04:00
|
|
|
if ((chp->ch_flags & (ATACH_TH_RESET | ATACH_SHUTDOWN)) == 0 &&
|
2004-08-04 22:24:10 +04:00
|
|
|
(chp->ch_queue->active_xfer == NULL ||
|
2003-12-30 19:28:37 +03:00
|
|
|
chp->ch_queue->queue_freeze == 0)) {
|
2008-11-16 22:31:21 +03:00
|
|
|
chp->ch_flags &= ~ATACH_TH_RUN;
|
2004-01-04 02:59:58 +03:00
|
|
|
(void) tsleep(&chp->ch_thread, PRIBIO, "atath", 0);
|
2008-11-16 22:31:21 +03:00
|
|
|
chp->ch_flags |= ATACH_TH_RUN;
|
2003-12-30 19:28:37 +03:00
|
|
|
}
|
2004-08-21 04:48:32 +04:00
|
|
|
if (chp->ch_flags & ATACH_SHUTDOWN) {
|
2003-12-30 19:28:37 +03:00
|
|
|
break;
|
2004-08-21 04:48:32 +04:00
|
|
|
}
|
2004-08-14 19:08:04 +04:00
|
|
|
if (chp->ch_flags & ATACH_TH_RESET) {
|
2004-08-03 02:02:35 +04:00
|
|
|
/*
|
2004-08-21 03:26:53 +04:00
|
|
|
* ata_reset_channel() will freeze 2 times, so
|
2004-08-03 02:02:35 +04:00
|
|
|
* unfreeze one time. Not a problem as we're at splbio
|
|
|
|
*/
|
2003-12-30 19:28:37 +03:00
|
|
|
chp->ch_queue->queue_freeze--;
|
2004-08-21 03:26:53 +04:00
|
|
|
ata_reset_channel(chp, AT_WAIT | chp->ch_reset_flags);
|
2004-08-04 22:24:10 +04:00
|
|
|
} else if (chp->ch_queue->active_xfer != NULL &&
|
2003-12-30 19:28:37 +03:00
|
|
|
chp->ch_queue->queue_freeze == 1) {
|
|
|
|
/*
|
|
|
|
* Caller has bumped queue_freeze, decrease it.
|
|
|
|
*/
|
|
|
|
chp->ch_queue->queue_freeze--;
|
2004-08-04 22:24:10 +04:00
|
|
|
xfer = chp->ch_queue->active_xfer;
|
2003-12-30 19:28:37 +03:00
|
|
|
KASSERT(xfer != NULL);
|
2004-10-29 00:11:41 +04:00
|
|
|
(*xfer->c_start)(xfer->c_chp, xfer);
|
2003-12-30 19:28:37 +03:00
|
|
|
} else if (chp->ch_queue->queue_freeze > 1)
|
|
|
|
panic("ata_thread: queue_freeze");
|
|
|
|
}
|
2005-05-20 18:39:04 +04:00
|
|
|
splx(s);
|
2004-01-04 02:59:58 +03:00
|
|
|
chp->ch_thread = NULL;
|
2005-05-30 02:11:28 +04:00
|
|
|
wakeup(&chp->ch_flags);
|
2003-12-30 19:28:37 +03:00
|
|
|
kthread_exit(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* atabus_match:
|
|
|
|
*
|
|
|
|
* Autoconfiguration match routine.
|
|
|
|
*/
|
|
|
|
static int
|
2008-01-10 10:41:20 +03:00
|
|
|
atabus_match(device_t parent, struct cfdata *cf, void *aux)
|
2003-12-30 19:28:37 +03:00
|
|
|
{
|
2004-08-14 19:08:04 +04:00
|
|
|
struct ata_channel *chp = aux;
|
2003-12-30 19:28:37 +03:00
|
|
|
|
|
|
|
if (chp == NULL)
|
|
|
|
return (0);
|
2005-02-27 03:26:58 +03:00
|
|
|
|
2004-01-04 01:56:52 +03:00
|
|
|
if (cf->cf_loc[ATACF_CHANNEL] != chp->ch_channel &&
|
2003-12-30 19:28:37 +03:00
|
|
|
cf->cf_loc[ATACF_CHANNEL] != ATACF_CHANNEL_DEFAULT)
|
2006-10-15 04:00:00 +04:00
|
|
|
return (0);
|
2005-02-27 03:26:58 +03:00
|
|
|
|
2003-12-30 19:28:37 +03:00
|
|
|
return (1);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* atabus_attach:
|
|
|
|
*
|
|
|
|
* Autoconfiguration attach routine.
|
|
|
|
*/
|
|
|
|
static void
|
2008-01-10 10:41:20 +03:00
|
|
|
atabus_attach(device_t parent, device_t self, void *aux)
|
2003-12-30 19:28:37 +03:00
|
|
|
{
|
2008-01-10 10:41:20 +03:00
|
|
|
struct atabus_softc *sc = device_private(self);
|
2004-08-14 19:08:04 +04:00
|
|
|
struct ata_channel *chp = aux;
|
2003-12-30 19:28:37 +03:00
|
|
|
struct atabus_initq *initq;
|
2007-07-10 00:51:58 +04:00
|
|
|
int error;
|
2003-12-30 19:28:37 +03:00
|
|
|
|
|
|
|
sc->sc_chan = chp;
|
|
|
|
|
|
|
|
aprint_normal("\n");
|
|
|
|
aprint_naive("\n");
|
|
|
|
|
2008-03-18 23:46:35 +03:00
|
|
|
sc->sc_dev = self;
|
|
|
|
|
2006-10-15 04:00:00 +04:00
|
|
|
if (ata_addref(chp))
|
|
|
|
return;
|
2004-08-11 03:09:38 +04:00
|
|
|
|
2003-12-30 19:28:37 +03:00
|
|
|
initq = malloc(sizeof(*initq), M_DEVBUF, M_WAITOK);
|
|
|
|
initq->atabus_sc = sc;
|
|
|
|
TAILQ_INSERT_TAIL(&atabus_initq_head, initq, atabus_initq);
|
|
|
|
config_pending_incr();
|
2007-07-10 00:51:58 +04:00
|
|
|
|
|
|
|
if ((error = kthread_create(PRI_NONE, 0, NULL, atabus_thread, sc,
|
2008-01-10 10:41:20 +03:00
|
|
|
&chp->ch_thread, "%s", device_xname(self))) != 0)
|
|
|
|
aprint_error_dev(self,
|
|
|
|
"unable to create kernel thread: error %d\n", error);
|
2005-01-27 00:51:40 +03:00
|
|
|
|
2007-12-09 23:27:42 +03:00
|
|
|
if (!pmf_device_register(self, atabus_suspend, atabus_resume))
|
|
|
|
aprint_error_dev(self, "couldn't establish power handler\n");
|
2003-12-30 19:28:37 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* atabus_activate:
|
|
|
|
*
|
|
|
|
* Autoconfiguration activation routine.
|
|
|
|
*/
|
|
|
|
static int
|
2008-01-10 10:41:20 +03:00
|
|
|
atabus_activate(device_t self, enum devact act)
|
2003-12-30 19:28:37 +03:00
|
|
|
{
|
2008-01-10 10:41:20 +03:00
|
|
|
struct atabus_softc *sc = device_private(self);
|
2004-08-14 19:08:04 +04:00
|
|
|
struct ata_channel *chp = sc->sc_chan;
|
2008-01-10 10:41:20 +03:00
|
|
|
device_t dev = NULL;
|
2003-12-30 19:28:37 +03:00
|
|
|
int s, i, error = 0;
|
|
|
|
|
|
|
|
s = splbio();
|
|
|
|
switch (act) {
|
|
|
|
case DVACT_ACTIVATE:
|
|
|
|
error = EOPNOTSUPP;
|
|
|
|
break;
|
2005-02-27 03:26:58 +03:00
|
|
|
|
2003-12-30 19:28:37 +03:00
|
|
|
case DVACT_DEACTIVATE:
|
|
|
|
/*
|
|
|
|
* We might deactivate the children of atapibus twice
|
|
|
|
* (once bia atapibus, once directly), but since the
|
|
|
|
* generic autoconfiguration code maintains the DVF_ACTIVE
|
|
|
|
* flag, it's safe.
|
|
|
|
*/
|
|
|
|
if ((dev = chp->atapibus) != NULL) {
|
|
|
|
error = config_deactivate(dev);
|
|
|
|
if (error)
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2004-08-14 19:08:04 +04:00
|
|
|
for (i = 0; i < chp->ch_ndrive; i++) {
|
2009-04-04 01:31:08 +04:00
|
|
|
if (chp->ch_drive[i].drive_flags & DRIVE_ATAPI)
|
|
|
|
continue;
|
2003-12-30 19:28:37 +03:00
|
|
|
if ((dev = chp->ch_drive[i].drv_softc) != NULL) {
|
2004-08-13 08:10:49 +04:00
|
|
|
ATADEBUG_PRINT(("atabus_activate: %s: "
|
2008-01-10 10:41:20 +03:00
|
|
|
"deactivating %s\n", device_xname(self),
|
|
|
|
device_xname(dev)),
|
2003-12-30 19:28:37 +03:00
|
|
|
DEBUG_DETACH);
|
|
|
|
error = config_deactivate(dev);
|
|
|
|
if (error)
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
out:
|
|
|
|
splx(s);
|
|
|
|
|
2004-08-13 08:10:49 +04:00
|
|
|
#ifdef ATADEBUG
|
2003-12-30 19:28:37 +03:00
|
|
|
if (dev != NULL && error != 0)
|
2004-08-13 08:10:49 +04:00
|
|
|
ATADEBUG_PRINT(("atabus_activate: %s: "
|
2008-01-10 10:41:20 +03:00
|
|
|
"error %d deactivating %s\n", device_xname(self),
|
|
|
|
error, device_xname(dev)), DEBUG_DETACH);
|
2004-08-13 08:10:49 +04:00
|
|
|
#endif /* ATADEBUG */
|
2003-12-30 19:28:37 +03:00
|
|
|
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* atabus_detach:
|
|
|
|
*
|
|
|
|
* Autoconfiguration detach routine.
|
|
|
|
*/
|
|
|
|
static int
|
2008-01-10 10:41:20 +03:00
|
|
|
atabus_detach(device_t self, int flags)
|
2003-12-30 19:28:37 +03:00
|
|
|
{
|
2008-01-10 10:41:20 +03:00
|
|
|
struct atabus_softc *sc = device_private(self);
|
2004-08-14 19:08:04 +04:00
|
|
|
struct ata_channel *chp = sc->sc_chan;
|
2008-01-10 10:41:20 +03:00
|
|
|
device_t dev = NULL;
|
2004-08-21 03:50:13 +04:00
|
|
|
int s, i, error = 0;
|
2003-12-30 19:28:37 +03:00
|
|
|
|
|
|
|
/* Shutdown the channel. */
|
2004-08-21 03:50:13 +04:00
|
|
|
s = splbio(); /* XXX ALSO NEED AN INTERLOCK HERE. */
|
2004-08-14 19:08:04 +04:00
|
|
|
chp->ch_flags |= ATACH_SHUTDOWN;
|
2004-08-21 03:50:13 +04:00
|
|
|
splx(s);
|
2008-01-10 10:41:20 +03:00
|
|
|
|
2004-01-04 02:59:58 +03:00
|
|
|
wakeup(&chp->ch_thread);
|
2008-01-10 10:41:20 +03:00
|
|
|
|
2004-01-04 02:59:58 +03:00
|
|
|
while (chp->ch_thread != NULL)
|
2005-05-30 02:11:28 +04:00
|
|
|
(void) tsleep(&chp->ch_flags, PRIBIO, "atadown", 0);
|
2005-02-27 03:26:58 +03:00
|
|
|
|
2008-01-10 10:41:20 +03:00
|
|
|
|
2003-12-30 19:28:37 +03:00
|
|
|
/*
|
|
|
|
* Detach atapibus and its children.
|
|
|
|
*/
|
|
|
|
if ((dev = chp->atapibus) != NULL) {
|
2004-08-13 08:10:49 +04:00
|
|
|
ATADEBUG_PRINT(("atabus_detach: %s: detaching %s\n",
|
2008-01-10 10:41:20 +03:00
|
|
|
device_xname(self), device_xname(dev)), DEBUG_DETACH);
|
|
|
|
|
2003-12-30 19:28:37 +03:00
|
|
|
error = config_detach(dev, flags);
|
|
|
|
if (error)
|
|
|
|
goto out;
|
2009-04-04 01:31:08 +04:00
|
|
|
KASSERT(chp->atapibus == NULL);
|
2003-12-30 19:28:37 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Detach our other children.
|
|
|
|
*/
|
2004-08-14 19:08:04 +04:00
|
|
|
for (i = 0; i < chp->ch_ndrive; i++) {
|
2003-12-30 19:28:37 +03:00
|
|
|
if (chp->ch_drive[i].drive_flags & DRIVE_ATAPI)
|
|
|
|
continue;
|
2009-04-04 01:31:08 +04:00
|
|
|
if ((dev = chp->ata_drives[i]) != NULL) {
|
|
|
|
ATADEBUG_PRINT(("%s.%d: %s: detaching %s\n", __func__,
|
|
|
|
__LINE__, device_xname(self), device_xname(dev)),
|
2003-12-30 19:28:37 +03:00
|
|
|
DEBUG_DETACH);
|
2009-04-04 01:31:08 +04:00
|
|
|
KASSERT(chp->ch_drive[i].drv_softc ==
|
|
|
|
chp->ata_drives[i]);
|
2003-12-30 19:28:37 +03:00
|
|
|
error = config_detach(dev, flags);
|
|
|
|
if (error)
|
|
|
|
goto out;
|
2009-04-04 01:31:08 +04:00
|
|
|
KASSERT(chp->ata_drives[i] == NULL);
|
2003-12-30 19:28:37 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
out:
|
2004-08-13 08:10:49 +04:00
|
|
|
#ifdef ATADEBUG
|
2003-12-30 19:28:37 +03:00
|
|
|
if (dev != NULL && error != 0)
|
2009-04-04 01:31:08 +04:00
|
|
|
ATADEBUG_PRINT(("%s: %s: error %d detaching %s\n", __func__,
|
2008-01-10 10:41:20 +03:00
|
|
|
device_xname(self), error, device_xname(dev)),
|
2003-12-30 19:28:37 +03:00
|
|
|
DEBUG_DETACH);
|
2004-08-13 08:10:49 +04:00
|
|
|
#endif /* ATADEBUG */
|
2003-12-30 19:28:37 +03:00
|
|
|
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
2008-01-10 10:41:20 +03:00
|
|
|
void
|
|
|
|
atabus_childdetached(device_t self, device_t child)
|
|
|
|
{
|
2009-04-04 01:31:08 +04:00
|
|
|
bool found = false;
|
2008-01-10 10:41:20 +03:00
|
|
|
struct atabus_softc *sc = device_private(self);
|
|
|
|
struct ata_channel *chp = sc->sc_chan;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* atapibus detached.
|
|
|
|
*/
|
|
|
|
if (child == chp->atapibus) {
|
|
|
|
chp->atapibus = NULL;
|
2009-04-04 01:31:08 +04:00
|
|
|
found = true;
|
2008-01-10 10:41:20 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Detach our other children.
|
|
|
|
*/
|
|
|
|
for (i = 0; i < chp->ch_ndrive; i++) {
|
|
|
|
if (chp->ch_drive[i].drive_flags & DRIVE_ATAPI)
|
|
|
|
continue;
|
2009-04-04 01:31:08 +04:00
|
|
|
if (child == chp->ata_drives[i]) {
|
|
|
|
KASSERT(chp->ata_drives[i] ==
|
|
|
|
chp->ch_drive[i].drv_softc);
|
|
|
|
chp->ata_drives[i] = NULL;
|
2008-01-10 10:41:20 +03:00
|
|
|
chp->ch_drive[i].drv_softc = NULL;
|
|
|
|
chp->ch_drive[i].drive_flags = 0;
|
2009-04-04 01:31:08 +04:00
|
|
|
found = true;
|
2008-01-10 10:41:20 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-04-04 01:31:08 +04:00
|
|
|
if (!found)
|
|
|
|
panic("%s: unknown child %p", device_xname(self),
|
|
|
|
(const void *)child);
|
2008-01-10 10:41:20 +03:00
|
|
|
}
|
|
|
|
|
During shutdown, detach devices in an orderly fashion.
Call the detach routine for every device in the device tree, starting
with the leaves and moving toward the root, expecting that each
(pseudo-)device driver will use the opportunity to gracefully commit
outstandings transactions to the underlying (pseudo-)device and to
relinquish control of the hardware to the system BIOS.
Detaching devices is not suitable for every shutdown: in an emergency,
or if the system state is inconsistent, we should resort to a fast,
simple shutdown that uses only the pmf(9) shutdown hooks and the
(deprecated) shutdownhooks. For now, if the flag RB_NOSYNC is set in
boothowto, opt for the fast, simple shutdown.
Add a device flag, DVF_DETACH_SHUTDOWN, that indicates by its presence
that it is safe to detach a device during shutdown. Introduce macros
CFATTACH_DECL3() and CFATTACH_DECL3_NEW() for creating autoconf
attachments with default device flags. Add DVF_DETACH_SHUTDOWN
to configuration attachments for atabus(4), atw(4) at cardbus(4),
cardbus(4), cardslot(4), com(4) at isa(4), elanpar(4), elanpex(4),
elansc(4), gpio(4), npx(4) at isa(4), nsphyter(4), pci(4), pcib(4),
pcmcia(4), ppb(4), sip(4), wd(4), and wdc(4) at isa(4).
Add a device-detachment "reason" flag, DETACH_SHUTDOWN, that tells the
autoconf code and a device driver that the reason for detachment is
system shutdown.
Add a sysctl, kern.detachall, that tells the system to try to detach
every device at shutdown, regardless of any device's DVF_DETACH_SHUTDOWN
flag. The default for kern.detachall is 0. SET IT TO 1, PLEASE, TO
HELP TEST AND DEBUG DEVICE DETACHMENT AT SHUTDOWN.
This is a work in progress. In future work, I aim to treat
pseudo-devices more thoroughly, and to gracefully tear down a stack of
(pseudo-)disk drivers and filesystems, including cgd(4), vnd(4), and
raid(4) instances at shutdown.
Also commit some changes that are not easily untangled from the rest:
(1) begin to simplify device_t locking: rename struct pmf_private to
device_lock, and incorporate device_lock into struct device.
(2) #include <sys/device.h> in sys/pmf.h in order to get some
definitions that it needs. Stop unnecessarily #including <sys/device.h>
in sys/arch/x86/include/pic.h to keep the amd64, xen, and i386 releases
building.
2009-04-02 04:09:32 +04:00
|
|
|
CFATTACH_DECL3_NEW(atabus, sizeof(struct atabus_softc),
|
2008-01-10 10:41:20 +03:00
|
|
|
atabus_match, atabus_attach, atabus_detach, atabus_activate, NULL,
|
During shutdown, detach devices in an orderly fashion.
Call the detach routine for every device in the device tree, starting
with the leaves and moving toward the root, expecting that each
(pseudo-)device driver will use the opportunity to gracefully commit
outstandings transactions to the underlying (pseudo-)device and to
relinquish control of the hardware to the system BIOS.
Detaching devices is not suitable for every shutdown: in an emergency,
or if the system state is inconsistent, we should resort to a fast,
simple shutdown that uses only the pmf(9) shutdown hooks and the
(deprecated) shutdownhooks. For now, if the flag RB_NOSYNC is set in
boothowto, opt for the fast, simple shutdown.
Add a device flag, DVF_DETACH_SHUTDOWN, that indicates by its presence
that it is safe to detach a device during shutdown. Introduce macros
CFATTACH_DECL3() and CFATTACH_DECL3_NEW() for creating autoconf
attachments with default device flags. Add DVF_DETACH_SHUTDOWN
to configuration attachments for atabus(4), atw(4) at cardbus(4),
cardbus(4), cardslot(4), com(4) at isa(4), elanpar(4), elanpex(4),
elansc(4), gpio(4), npx(4) at isa(4), nsphyter(4), pci(4), pcib(4),
pcmcia(4), ppb(4), sip(4), wd(4), and wdc(4) at isa(4).
Add a device-detachment "reason" flag, DETACH_SHUTDOWN, that tells the
autoconf code and a device driver that the reason for detachment is
system shutdown.
Add a sysctl, kern.detachall, that tells the system to try to detach
every device at shutdown, regardless of any device's DVF_DETACH_SHUTDOWN
flag. The default for kern.detachall is 0. SET IT TO 1, PLEASE, TO
HELP TEST AND DEBUG DEVICE DETACHMENT AT SHUTDOWN.
This is a work in progress. In future work, I aim to treat
pseudo-devices more thoroughly, and to gracefully tear down a stack of
(pseudo-)disk drivers and filesystems, including cgd(4), vnd(4), and
raid(4) instances at shutdown.
Also commit some changes that are not easily untangled from the rest:
(1) begin to simplify device_t locking: rename struct pmf_private to
device_lock, and incorporate device_lock into struct device.
(2) #include <sys/device.h> in sys/pmf.h in order to get some
definitions that it needs. Stop unnecessarily #including <sys/device.h>
in sys/arch/x86/include/pic.h to keep the amd64, xen, and i386 releases
building.
2009-04-02 04:09:32 +04:00
|
|
|
atabus_childdetached, DVF_DETACH_SHUTDOWN);
|
2003-12-30 19:28:37 +03:00
|
|
|
|
|
|
|
/*****************************************************************************
|
|
|
|
* Common ATA bus operations.
|
|
|
|
*****************************************************************************/
|
|
|
|
|
1998-10-12 20:09:10 +04:00
|
|
|
/* Get the disk's parameters */
|
|
|
|
int
|
2003-12-14 05:45:48 +03:00
|
|
|
ata_get_params(struct ata_drive_datas *drvp, u_int8_t flags,
|
|
|
|
struct ataparams *prms)
|
1998-10-12 20:09:10 +04:00
|
|
|
{
|
2004-08-12 08:57:19 +04:00
|
|
|
struct ata_command ata_c;
|
2004-08-21 00:53:20 +04:00
|
|
|
struct ata_channel *chp = drvp->chnl_softc;
|
|
|
|
struct atac_softc *atac = chp->ch_atac;
|
2007-12-20 23:12:01 +03:00
|
|
|
char *tb;
|
|
|
|
int i, rv;
|
1998-10-12 20:09:10 +04:00
|
|
|
u_int16_t *p;
|
|
|
|
|
2007-03-17 09:41:35 +03:00
|
|
|
ATADEBUG_PRINT(("%s\n", __func__), DEBUG_FUNCS);
|
1998-10-12 20:09:10 +04:00
|
|
|
|
2007-12-20 23:12:01 +03:00
|
|
|
tb = kmem_zalloc(DEV_BSIZE, KM_SLEEP);
|
1998-10-12 20:09:10 +04:00
|
|
|
memset(prms, 0, sizeof(struct ataparams));
|
2004-08-12 08:57:19 +04:00
|
|
|
memset(&ata_c, 0, sizeof(struct ata_command));
|
1998-10-12 20:09:10 +04:00
|
|
|
|
|
|
|
if (drvp->drive_flags & DRIVE_ATA) {
|
2004-08-12 08:57:19 +04:00
|
|
|
ata_c.r_command = WDCC_IDENTIFY;
|
|
|
|
ata_c.r_st_bmask = WDCS_DRDY;
|
2006-01-22 19:40:56 +03:00
|
|
|
ata_c.r_st_pmask = WDCS_DRQ;
|
2004-08-12 08:57:19 +04:00
|
|
|
ata_c.timeout = 3000; /* 3s */
|
1999-03-10 16:11:43 +03:00
|
|
|
} else if (drvp->drive_flags & DRIVE_ATAPI) {
|
2004-08-12 08:57:19 +04:00
|
|
|
ata_c.r_command = ATAPI_IDENTIFY_DEVICE;
|
|
|
|
ata_c.r_st_bmask = 0;
|
2006-01-22 19:40:56 +03:00
|
|
|
ata_c.r_st_pmask = WDCS_DRQ;
|
2004-08-12 08:57:19 +04:00
|
|
|
ata_c.timeout = 10000; /* 10s */
|
1999-03-10 16:11:43 +03:00
|
|
|
} else {
|
2004-08-13 08:10:49 +04:00
|
|
|
ATADEBUG_PRINT(("ata_get_parms: no disks\n"),
|
1999-11-26 15:39:43 +03:00
|
|
|
DEBUG_FUNCS|DEBUG_PROBE);
|
2007-12-20 23:12:01 +03:00
|
|
|
rv = CMD_ERR;
|
|
|
|
goto out;
|
1998-10-12 20:09:10 +04:00
|
|
|
}
|
2004-08-12 08:57:19 +04:00
|
|
|
ata_c.flags = AT_READ | flags;
|
|
|
|
ata_c.data = tb;
|
|
|
|
ata_c.bcount = DEV_BSIZE;
|
2004-08-21 00:53:20 +04:00
|
|
|
if ((*atac->atac_bustype_ata->ata_exec_command)(drvp,
|
|
|
|
&ata_c) != ATACMD_COMPLETE) {
|
2004-08-13 08:10:49 +04:00
|
|
|
ATADEBUG_PRINT(("ata_get_parms: wdc_exec_command failed\n"),
|
1999-11-26 15:39:43 +03:00
|
|
|
DEBUG_FUNCS|DEBUG_PROBE);
|
2007-12-20 23:12:01 +03:00
|
|
|
rv = CMD_AGAIN;
|
|
|
|
goto out;
|
1999-11-26 15:39:43 +03:00
|
|
|
}
|
2004-08-12 08:57:19 +04:00
|
|
|
if (ata_c.flags & (AT_ERROR | AT_TIMEOU | AT_DF)) {
|
2004-08-13 08:10:49 +04:00
|
|
|
ATADEBUG_PRINT(("ata_get_parms: ata_c.flags=0x%x\n",
|
2004-08-12 08:57:19 +04:00
|
|
|
ata_c.flags), DEBUG_FUNCS|DEBUG_PROBE);
|
2007-12-20 23:12:01 +03:00
|
|
|
rv = CMD_ERR;
|
|
|
|
goto out;
|
2007-07-01 13:47:19 +04:00
|
|
|
}
|
|
|
|
/* if we didn't read any data something is wrong */
|
2007-12-20 23:12:01 +03:00
|
|
|
if ((ata_c.flags & AT_XFDONE) == 0) {
|
|
|
|
rv = CMD_ERR;
|
|
|
|
goto out;
|
|
|
|
}
|
2006-10-15 03:54:14 +04:00
|
|
|
|
2007-07-01 13:47:19 +04:00
|
|
|
/* Read in parameter block. */
|
|
|
|
memcpy(prms, tb, sizeof(struct ataparams));
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Shuffle string byte order.
|
|
|
|
* ATAPI NEC, Mitsumi and Pioneer drives and
|
|
|
|
* old ATA TDK CompactFlash cards
|
|
|
|
* have different byte order.
|
|
|
|
*/
|
2006-10-15 03:54:14 +04:00
|
|
|
#if BYTE_ORDER == BIG_ENDIAN
|
|
|
|
# define M(n) prms->atap_model[(n) ^ 1]
|
|
|
|
#else
|
|
|
|
# define M(n) prms->atap_model[n]
|
|
|
|
#endif
|
2007-07-01 13:47:19 +04:00
|
|
|
if (
|
2006-10-15 03:54:14 +04:00
|
|
|
#if BYTE_ORDER == BIG_ENDIAN
|
2007-07-01 13:47:19 +04:00
|
|
|
!
|
2006-10-15 03:54:14 +04:00
|
|
|
#endif
|
2007-07-01 13:47:19 +04:00
|
|
|
((drvp->drive_flags & DRIVE_ATAPI) ?
|
|
|
|
((M(0) == 'N' && M(1) == 'E') ||
|
|
|
|
(M(0) == 'F' && M(1) == 'X') ||
|
|
|
|
(M(0) == 'P' && M(1) == 'i')) :
|
2007-12-20 23:12:01 +03:00
|
|
|
((M(0) == 'T' && M(1) == 'D' && M(2) == 'K')))) {
|
|
|
|
rv = CMD_OK;
|
|
|
|
goto out;
|
|
|
|
}
|
2007-07-01 13:47:19 +04:00
|
|
|
#undef M
|
|
|
|
for (i = 0; i < sizeof(prms->atap_model); i += 2) {
|
|
|
|
p = (u_int16_t *)(prms->atap_model + i);
|
|
|
|
*p = bswap16(*p);
|
1998-10-12 20:09:10 +04:00
|
|
|
}
|
2007-07-01 13:47:19 +04:00
|
|
|
for (i = 0; i < sizeof(prms->atap_serial); i += 2) {
|
|
|
|
p = (u_int16_t *)(prms->atap_serial + i);
|
|
|
|
*p = bswap16(*p);
|
|
|
|
}
|
|
|
|
for (i = 0; i < sizeof(prms->atap_revision); i += 2) {
|
|
|
|
p = (u_int16_t *)(prms->atap_revision + i);
|
|
|
|
*p = bswap16(*p);
|
|
|
|
}
|
|
|
|
|
2007-12-20 23:12:01 +03:00
|
|
|
rv = CMD_OK;
|
|
|
|
out:
|
|
|
|
kmem_free(tb, DEV_BSIZE);
|
|
|
|
return rv;
|
1998-10-12 20:09:10 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
int
|
2003-12-14 05:45:48 +03:00
|
|
|
ata_set_mode(struct ata_drive_datas *drvp, u_int8_t mode, u_int8_t flags)
|
1998-10-12 20:09:10 +04:00
|
|
|
{
|
2004-08-12 08:57:19 +04:00
|
|
|
struct ata_command ata_c;
|
2004-08-21 00:53:20 +04:00
|
|
|
struct ata_channel *chp = drvp->chnl_softc;
|
|
|
|
struct atac_softc *atac = chp->ch_atac;
|
1998-10-12 20:09:10 +04:00
|
|
|
|
2004-08-13 08:10:49 +04:00
|
|
|
ATADEBUG_PRINT(("ata_set_mode=0x%x\n", mode), DEBUG_FUNCS);
|
2004-08-12 08:57:19 +04:00
|
|
|
memset(&ata_c, 0, sizeof(struct ata_command));
|
|
|
|
|
|
|
|
ata_c.r_command = SET_FEATURES;
|
|
|
|
ata_c.r_st_bmask = 0;
|
|
|
|
ata_c.r_st_pmask = 0;
|
|
|
|
ata_c.r_features = WDSF_SET_MODE;
|
|
|
|
ata_c.r_count = mode;
|
|
|
|
ata_c.flags = flags;
|
|
|
|
ata_c.timeout = 1000; /* 1s */
|
2004-08-21 00:53:20 +04:00
|
|
|
if ((*atac->atac_bustype_ata->ata_exec_command)(drvp,
|
|
|
|
&ata_c) != ATACMD_COMPLETE)
|
1998-10-12 20:09:10 +04:00
|
|
|
return CMD_AGAIN;
|
2004-08-12 08:57:19 +04:00
|
|
|
if (ata_c.flags & (AT_ERROR | AT_TIMEOU | AT_DF)) {
|
1998-10-12 20:09:10 +04:00
|
|
|
return CMD_ERR;
|
|
|
|
}
|
|
|
|
return CMD_OK;
|
|
|
|
}
|
1999-01-18 23:06:24 +03:00
|
|
|
|
2006-09-30 19:56:17 +04:00
|
|
|
#if NATA_DMA
|
2000-01-17 03:01:00 +03:00
|
|
|
void
|
2003-12-14 05:45:48 +03:00
|
|
|
ata_dmaerr(struct ata_drive_datas *drvp, int flags)
|
2000-01-17 03:01:00 +03:00
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Downgrade decision: if we get NERRS_MAX in NXFER.
|
|
|
|
* We start with n_dmaerrs set to NERRS_MAX-1 so that the
|
|
|
|
* first error within the first NXFER ops will immediatly trigger
|
|
|
|
* a downgrade.
|
|
|
|
* If we got an error and n_xfers is bigger than NXFER reset counters.
|
|
|
|
*/
|
|
|
|
drvp->n_dmaerrs++;
|
|
|
|
if (drvp->n_dmaerrs >= NERRS_MAX && drvp->n_xfers <= NXFER) {
|
2004-08-13 01:05:09 +04:00
|
|
|
ata_downgrade_mode(drvp, flags);
|
2000-01-17 03:01:00 +03:00
|
|
|
drvp->n_dmaerrs = NERRS_MAX-1;
|
|
|
|
drvp->n_xfers = 0;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (drvp->n_xfers > NXFER) {
|
|
|
|
drvp->n_dmaerrs = 1; /* just got an error */
|
|
|
|
drvp->n_xfers = 1; /* restart counting from this error */
|
|
|
|
}
|
|
|
|
}
|
2006-09-30 19:56:17 +04:00
|
|
|
#endif /* NATA_DMA */
|
2004-08-02 01:40:41 +04:00
|
|
|
|
2005-05-17 01:43:33 +04:00
|
|
|
/*
|
|
|
|
* freeze the queue and wait for the controller to be idle. Caller has to
|
|
|
|
* unfreeze/restart the queue
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
ata_queue_idle(struct ata_queue *queue)
|
|
|
|
{
|
|
|
|
int s = splbio();
|
|
|
|
queue->queue_freeze++;
|
|
|
|
while (queue->active_xfer != NULL) {
|
|
|
|
queue->queue_flags |= QF_IDLE_WAIT;
|
|
|
|
tsleep(&queue->queue_flags, PRIBIO, "qidl", 0);
|
|
|
|
}
|
|
|
|
splx(s);
|
|
|
|
}
|
|
|
|
|
2004-08-13 06:10:43 +04:00
|
|
|
/*
|
2004-08-21 03:50:13 +04:00
|
|
|
* Add a command to the queue and start controller.
|
|
|
|
*
|
|
|
|
* MUST BE CALLED AT splbio()!
|
2004-08-13 06:10:43 +04:00
|
|
|
*/
|
|
|
|
void
|
2004-08-14 19:08:04 +04:00
|
|
|
ata_exec_xfer(struct ata_channel *chp, struct ata_xfer *xfer)
|
2004-08-13 06:10:43 +04:00
|
|
|
{
|
|
|
|
|
2004-08-13 08:10:49 +04:00
|
|
|
ATADEBUG_PRINT(("ata_exec_xfer %p channel %d drive %d\n", xfer,
|
2004-08-13 06:10:43 +04:00
|
|
|
chp->ch_channel, xfer->c_drive), DEBUG_XFERS);
|
|
|
|
|
|
|
|
/* complete xfer setup */
|
|
|
|
xfer->c_chp = chp;
|
|
|
|
|
|
|
|
/* insert at the end of command list */
|
|
|
|
TAILQ_INSERT_TAIL(&chp->ch_queue->queue_xfer, xfer, c_xferchain);
|
2004-08-13 08:10:49 +04:00
|
|
|
ATADEBUG_PRINT(("atastart from ata_exec_xfer, flags 0x%x\n",
|
2004-08-13 06:10:43 +04:00
|
|
|
chp->ch_flags), DEBUG_XFERS);
|
2004-10-31 02:10:37 +03:00
|
|
|
/*
|
|
|
|
* if polling and can sleep, wait for the xfer to be at head of queue
|
|
|
|
*/
|
|
|
|
if ((xfer->c_flags & (C_POLL | C_WAIT)) == (C_POLL | C_WAIT)) {
|
|
|
|
while (chp->ch_queue->active_xfer != NULL ||
|
|
|
|
TAILQ_FIRST(&chp->ch_queue->queue_xfer) != xfer) {
|
|
|
|
xfer->c_flags |= C_WAITACT;
|
|
|
|
tsleep(xfer, PRIBIO, "ataact", 0);
|
|
|
|
xfer->c_flags &= ~C_WAITACT;
|
|
|
|
if (xfer->c_flags & C_FREE) {
|
|
|
|
ata_free_xfer(chp, xfer);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2004-08-13 06:16:40 +04:00
|
|
|
atastart(chp);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Start I/O on a controller, for the given channel.
|
|
|
|
* The first xfer may be not for our channel if the channel queues
|
|
|
|
* are shared.
|
2004-08-21 03:50:13 +04:00
|
|
|
*
|
|
|
|
* MUST BE CALLED AT splbio()!
|
2004-08-13 06:16:40 +04:00
|
|
|
*/
|
|
|
|
void
|
2004-08-14 19:08:04 +04:00
|
|
|
atastart(struct ata_channel *chp)
|
2004-08-13 06:16:40 +04:00
|
|
|
{
|
2004-08-20 10:39:37 +04:00
|
|
|
struct atac_softc *atac = chp->ch_atac;
|
2004-08-13 06:16:40 +04:00
|
|
|
struct ata_xfer *xfer;
|
|
|
|
|
2004-08-21 03:36:52 +04:00
|
|
|
#ifdef ATA_DEBUG
|
2004-08-13 06:16:40 +04:00
|
|
|
int spl1, spl2;
|
|
|
|
|
|
|
|
spl1 = splbio();
|
|
|
|
spl2 = splbio();
|
|
|
|
if (spl2 != spl1) {
|
|
|
|
printf("atastart: not at splbio()\n");
|
|
|
|
panic("atastart");
|
|
|
|
}
|
|
|
|
splx(spl2);
|
|
|
|
splx(spl1);
|
2004-08-21 03:36:52 +04:00
|
|
|
#endif /* ATA_DEBUG */
|
2004-08-13 06:16:40 +04:00
|
|
|
|
|
|
|
/* is there a xfer ? */
|
|
|
|
if ((xfer = TAILQ_FIRST(&chp->ch_queue->queue_xfer)) == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* adjust chp, in case we have a shared queue */
|
|
|
|
chp = xfer->c_chp;
|
|
|
|
|
|
|
|
if (chp->ch_queue->active_xfer != NULL) {
|
|
|
|
return; /* channel aleady active */
|
|
|
|
}
|
|
|
|
if (__predict_false(chp->ch_queue->queue_freeze > 0)) {
|
2005-05-17 01:43:33 +04:00
|
|
|
if (chp->ch_queue->queue_flags & QF_IDLE_WAIT) {
|
|
|
|
chp->ch_queue->queue_flags &= ~QF_IDLE_WAIT;
|
|
|
|
wakeup(&chp->ch_queue->queue_flags);
|
|
|
|
}
|
|
|
|
return; /* queue frozen */
|
2004-08-13 06:16:40 +04:00
|
|
|
}
|
2004-10-31 02:10:37 +03:00
|
|
|
/*
|
|
|
|
* if someone is waiting for the command to be active, wake it up
|
|
|
|
* and let it process the command
|
|
|
|
*/
|
|
|
|
if (xfer->c_flags & C_WAITACT) {
|
|
|
|
ATADEBUG_PRINT(("atastart: xfer %p channel %d drive %d "
|
|
|
|
"wait active\n", xfer, chp->ch_channel, xfer->c_drive),
|
|
|
|
DEBUG_XFERS);
|
|
|
|
wakeup(xfer);
|
|
|
|
return;
|
|
|
|
}
|
2004-08-13 06:16:40 +04:00
|
|
|
#ifdef DIAGNOSTIC
|
2004-08-14 19:08:04 +04:00
|
|
|
if ((chp->ch_flags & ATACH_IRQ_WAIT) != 0)
|
2004-08-13 06:16:40 +04:00
|
|
|
panic("atastart: channel waiting for irq");
|
|
|
|
#endif
|
2004-08-20 10:39:37 +04:00
|
|
|
if (atac->atac_claim_hw)
|
|
|
|
if (!(*atac->atac_claim_hw)(chp, 0))
|
2004-08-13 06:16:40 +04:00
|
|
|
return;
|
|
|
|
|
2004-08-13 08:10:49 +04:00
|
|
|
ATADEBUG_PRINT(("atastart: xfer %p channel %d drive %d\n", xfer,
|
2004-08-13 06:16:40 +04:00
|
|
|
chp->ch_channel, xfer->c_drive), DEBUG_XFERS);
|
|
|
|
if (chp->ch_drive[xfer->c_drive].drive_flags & DRIVE_RESET) {
|
|
|
|
chp->ch_drive[xfer->c_drive].drive_flags &= ~DRIVE_RESET;
|
|
|
|
chp->ch_drive[xfer->c_drive].state = 0;
|
|
|
|
}
|
|
|
|
chp->ch_queue->active_xfer = xfer;
|
|
|
|
TAILQ_REMOVE(&chp->ch_queue->queue_xfer, xfer, c_xferchain);
|
2005-02-27 03:26:58 +03:00
|
|
|
|
2004-08-20 10:39:37 +04:00
|
|
|
if (atac->atac_cap & ATAC_CAP_NOIRQ)
|
2004-08-13 06:16:40 +04:00
|
|
|
KASSERT(xfer->c_flags & C_POLL);
|
2004-10-31 02:10:37 +03:00
|
|
|
|
2004-08-13 06:16:40 +04:00
|
|
|
xfer->c_start(chp, xfer);
|
2004-08-13 06:10:43 +04:00
|
|
|
}
|
|
|
|
|
2004-08-13 01:34:52 +04:00
|
|
|
struct ata_xfer *
|
|
|
|
ata_get_xfer(int flags)
|
|
|
|
{
|
|
|
|
struct ata_xfer *xfer;
|
|
|
|
int s;
|
|
|
|
|
|
|
|
s = splbio();
|
|
|
|
xfer = pool_get(&ata_xfer_pool,
|
|
|
|
((flags & ATAXF_NOSLEEP) != 0 ? PR_NOWAIT : PR_WAITOK));
|
|
|
|
splx(s);
|
|
|
|
if (xfer != NULL) {
|
|
|
|
memset(xfer, 0, sizeof(struct ata_xfer));
|
|
|
|
}
|
|
|
|
return xfer;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2004-08-14 19:08:04 +04:00
|
|
|
ata_free_xfer(struct ata_channel *chp, struct ata_xfer *xfer)
|
2004-08-13 01:34:52 +04:00
|
|
|
{
|
2004-08-20 10:39:37 +04:00
|
|
|
struct atac_softc *atac = chp->ch_atac;
|
2004-08-13 01:34:52 +04:00
|
|
|
int s;
|
|
|
|
|
2004-10-31 02:10:37 +03:00
|
|
|
if (xfer->c_flags & C_WAITACT) {
|
|
|
|
/* Someone is waiting for this xfer, so we can't free now */
|
|
|
|
xfer->c_flags |= C_FREE;
|
|
|
|
wakeup(xfer);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2006-09-07 16:34:41 +04:00
|
|
|
#if NATA_PIOBM /* XXX wdc dependent code */
|
|
|
|
if (xfer->c_flags & C_PIOBM) {
|
|
|
|
struct wdc_softc *wdc = CHAN_TO_WDC(chp);
|
|
|
|
|
|
|
|
/* finish the busmastering PIO */
|
|
|
|
(*wdc->piobm_done)(wdc->dma_arg,
|
|
|
|
chp->ch_channel, xfer->c_drive);
|
|
|
|
chp->ch_flags &= ~(ATACH_DMA_WAIT | ATACH_PIOBM_WAIT | ATACH_IRQ_WAIT);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2004-08-20 10:39:37 +04:00
|
|
|
if (atac->atac_free_hw)
|
|
|
|
(*atac->atac_free_hw)(chp);
|
2004-08-13 01:34:52 +04:00
|
|
|
s = splbio();
|
|
|
|
pool_put(&ata_xfer_pool, xfer);
|
|
|
|
splx(s);
|
|
|
|
}
|
|
|
|
|
2004-08-13 02:33:45 +04:00
|
|
|
/*
|
2004-08-14 19:08:04 +04:00
|
|
|
* Kill off all pending xfers for a ata_channel.
|
2004-08-13 02:33:45 +04:00
|
|
|
*
|
|
|
|
* Must be called at splbio().
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
ata_kill_pending(struct ata_drive_datas *drvp)
|
|
|
|
{
|
2004-08-14 19:08:04 +04:00
|
|
|
struct ata_channel *chp = drvp->chnl_softc;
|
2004-08-13 02:33:45 +04:00
|
|
|
struct ata_xfer *xfer, *next_xfer;
|
|
|
|
int s = splbio();
|
|
|
|
|
|
|
|
for (xfer = TAILQ_FIRST(&chp->ch_queue->queue_xfer);
|
|
|
|
xfer != NULL; xfer = next_xfer) {
|
|
|
|
next_xfer = TAILQ_NEXT(xfer, c_xferchain);
|
|
|
|
if (xfer->c_chp != chp || xfer->c_drive != drvp->drive)
|
|
|
|
continue;
|
|
|
|
TAILQ_REMOVE(&chp->ch_queue->queue_xfer, xfer, c_xferchain);
|
|
|
|
(*xfer->c_kill_xfer)(chp, xfer, KILL_GONE);
|
|
|
|
}
|
|
|
|
|
|
|
|
while ((xfer = chp->ch_queue->active_xfer) != NULL) {
|
|
|
|
if (xfer->c_chp == chp && xfer->c_drive == drvp->drive) {
|
|
|
|
drvp->drive_flags |= DRIVE_WAITDRAIN;
|
|
|
|
(void) tsleep(&chp->ch_queue->active_xfer,
|
|
|
|
PRIBIO, "atdrn", 0);
|
|
|
|
} else {
|
|
|
|
/* no more xfer for us */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
splx(s);
|
|
|
|
}
|
|
|
|
|
2004-08-21 03:26:53 +04:00
|
|
|
/*
|
|
|
|
* ata_reset_channel:
|
|
|
|
*
|
|
|
|
* Reset and ATA channel.
|
2004-08-21 03:50:13 +04:00
|
|
|
*
|
|
|
|
* MUST BE CALLED AT splbio()!
|
2004-08-21 03:26:53 +04:00
|
|
|
*/
|
|
|
|
void
|
|
|
|
ata_reset_channel(struct ata_channel *chp, int flags)
|
|
|
|
{
|
|
|
|
struct atac_softc *atac = chp->ch_atac;
|
|
|
|
int drive;
|
|
|
|
|
2004-08-21 03:50:13 +04:00
|
|
|
#ifdef ATA_DEBUG
|
|
|
|
int spl1, spl2;
|
|
|
|
|
|
|
|
spl1 = splbio();
|
|
|
|
spl2 = splbio();
|
|
|
|
if (spl2 != spl1) {
|
|
|
|
printf("ata_reset_channel: not at splbio()\n");
|
|
|
|
panic("ata_reset_channel");
|
|
|
|
}
|
|
|
|
splx(spl2);
|
|
|
|
splx(spl1);
|
|
|
|
#endif /* ATA_DEBUG */
|
|
|
|
|
2004-08-21 03:26:53 +04:00
|
|
|
chp->ch_queue->queue_freeze++;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If we can poll or wait it's OK, otherwise wake up the
|
|
|
|
* kernel thread to do it for us.
|
|
|
|
*/
|
2008-10-01 22:23:55 +04:00
|
|
|
ATADEBUG_PRINT(("ata_reset_channel flags 0x%x ch_flags 0x%x\n",
|
|
|
|
flags, chp->ch_flags), DEBUG_FUNCS | DEBUG_XFERS);
|
2004-08-21 03:26:53 +04:00
|
|
|
if ((flags & (AT_POLL | AT_WAIT)) == 0) {
|
|
|
|
if (chp->ch_flags & ATACH_TH_RESET) {
|
|
|
|
/* No need to schedule a reset more than one time. */
|
2004-09-11 22:01:58 +04:00
|
|
|
chp->ch_queue->queue_freeze--;
|
2004-08-21 03:26:53 +04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
chp->ch_flags |= ATACH_TH_RESET;
|
|
|
|
chp->ch_reset_flags = flags & (AT_RST_EMERG | AT_RST_NOCMD);
|
|
|
|
wakeup(&chp->ch_thread);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
(*atac->atac_bustype_ata->ata_reset_channel)(chp, flags);
|
|
|
|
|
|
|
|
for (drive = 0; drive < chp->ch_ndrive; drive++)
|
|
|
|
chp->ch_drive[drive].state = 0;
|
|
|
|
|
|
|
|
chp->ch_flags &= ~ATACH_TH_RESET;
|
|
|
|
if ((flags & AT_RST_EMERG) == 0) {
|
|
|
|
chp->ch_queue->queue_freeze--;
|
|
|
|
atastart(chp);
|
|
|
|
} else {
|
|
|
|
/* make sure that we can use polled commands */
|
|
|
|
TAILQ_INIT(&chp->ch_queue->queue_xfer);
|
|
|
|
chp->ch_queue->queue_freeze = 0;
|
|
|
|
chp->ch_queue->active_xfer = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-08-13 02:39:40 +04:00
|
|
|
int
|
2004-08-14 19:08:04 +04:00
|
|
|
ata_addref(struct ata_channel *chp)
|
2004-08-13 02:39:40 +04:00
|
|
|
{
|
2005-02-27 03:26:58 +03:00
|
|
|
struct atac_softc *atac = chp->ch_atac;
|
2004-08-20 10:39:37 +04:00
|
|
|
struct scsipi_adapter *adapt = &atac->atac_atapi_adapter._generic;
|
2004-08-13 02:39:40 +04:00
|
|
|
int s, error = 0;
|
|
|
|
|
|
|
|
s = splbio();
|
|
|
|
if (adapt->adapt_refcnt++ == 0 &&
|
|
|
|
adapt->adapt_enable != NULL) {
|
2008-03-18 23:46:35 +03:00
|
|
|
error = (*adapt->adapt_enable)(atac->atac_dev, 1);
|
2004-08-13 02:39:40 +04:00
|
|
|
if (error)
|
|
|
|
adapt->adapt_refcnt--;
|
|
|
|
}
|
|
|
|
splx(s);
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2004-08-14 19:08:04 +04:00
|
|
|
ata_delref(struct ata_channel *chp)
|
2004-08-13 02:39:40 +04:00
|
|
|
{
|
2004-08-20 10:39:37 +04:00
|
|
|
struct atac_softc *atac = chp->ch_atac;
|
|
|
|
struct scsipi_adapter *adapt = &atac->atac_atapi_adapter._generic;
|
2004-08-13 02:39:40 +04:00
|
|
|
int s;
|
|
|
|
|
|
|
|
s = splbio();
|
|
|
|
if (adapt->adapt_refcnt-- == 1 &&
|
|
|
|
adapt->adapt_enable != NULL)
|
2008-03-18 23:46:35 +03:00
|
|
|
(void) (*adapt->adapt_enable)(atac->atac_dev, 0);
|
2004-08-13 02:39:40 +04:00
|
|
|
splx(s);
|
|
|
|
}
|
|
|
|
|
2004-08-13 00:59:27 +04:00
|
|
|
void
|
2004-08-14 19:08:04 +04:00
|
|
|
ata_print_modes(struct ata_channel *chp)
|
2004-08-13 00:59:27 +04:00
|
|
|
{
|
2004-08-20 10:39:37 +04:00
|
|
|
struct atac_softc *atac = chp->ch_atac;
|
2004-08-13 00:59:27 +04:00
|
|
|
int drive;
|
|
|
|
struct ata_drive_datas *drvp;
|
|
|
|
|
2005-04-11 08:24:54 +04:00
|
|
|
for (drive = 0; drive < chp->ch_ndrive; drive++) {
|
2004-08-13 00:59:27 +04:00
|
|
|
drvp = &chp->ch_drive[drive];
|
2005-04-11 08:24:54 +04:00
|
|
|
if ((drvp->drive_flags & DRIVE) == 0 || drvp->drv_softc == NULL)
|
2004-08-13 00:59:27 +04:00
|
|
|
continue;
|
2007-02-10 00:55:00 +03:00
|
|
|
aprint_verbose("%s(%s:%d:%d): using PIO mode %d",
|
2008-01-10 10:41:20 +03:00
|
|
|
device_xname(drvp->drv_softc),
|
2008-03-18 23:46:35 +03:00
|
|
|
device_xname(atac->atac_dev),
|
2005-04-11 08:24:54 +04:00
|
|
|
chp->ch_channel, drvp->drive, drvp->PIO_mode);
|
2006-09-30 19:56:17 +04:00
|
|
|
#if NATA_DMA
|
2004-08-13 00:59:27 +04:00
|
|
|
if (drvp->drive_flags & DRIVE_DMA)
|
2007-02-10 00:55:00 +03:00
|
|
|
aprint_verbose(", DMA mode %d", drvp->DMA_mode);
|
2006-09-30 19:56:17 +04:00
|
|
|
#if NATA_UDMA
|
2004-08-13 00:59:27 +04:00
|
|
|
if (drvp->drive_flags & DRIVE_UDMA) {
|
2007-02-10 00:55:00 +03:00
|
|
|
aprint_verbose(", Ultra-DMA mode %d", drvp->UDMA_mode);
|
2004-08-13 00:59:27 +04:00
|
|
|
if (drvp->UDMA_mode == 2)
|
2007-02-10 00:55:00 +03:00
|
|
|
aprint_verbose(" (Ultra/33)");
|
2004-08-13 00:59:27 +04:00
|
|
|
else if (drvp->UDMA_mode == 4)
|
2007-02-10 00:55:00 +03:00
|
|
|
aprint_verbose(" (Ultra/66)");
|
2004-08-13 00:59:27 +04:00
|
|
|
else if (drvp->UDMA_mode == 5)
|
2007-02-10 00:55:00 +03:00
|
|
|
aprint_verbose(" (Ultra/100)");
|
2004-08-13 00:59:27 +04:00
|
|
|
else if (drvp->UDMA_mode == 6)
|
2007-02-10 00:55:00 +03:00
|
|
|
aprint_verbose(" (Ultra/133)");
|
2004-08-13 00:59:27 +04:00
|
|
|
}
|
2006-09-30 19:56:17 +04:00
|
|
|
#endif /* NATA_UDMA */
|
|
|
|
#endif /* NATA_DMA */
|
|
|
|
#if NATA_DMA || NATA_PIOBM
|
|
|
|
if (0
|
|
|
|
#if NATA_DMA
|
|
|
|
|| (drvp->drive_flags & (DRIVE_DMA | DRIVE_UDMA))
|
|
|
|
#endif
|
2006-09-07 16:34:41 +04:00
|
|
|
#if NATA_PIOBM
|
|
|
|
/* PIOBM capable controllers use DMA for PIO commands */
|
|
|
|
|| (atac->atac_cap & ATAC_CAP_PIOBM)
|
|
|
|
#endif
|
|
|
|
)
|
2007-02-10 00:55:00 +03:00
|
|
|
aprint_verbose(" (using DMA)");
|
2006-09-30 19:56:17 +04:00
|
|
|
#endif /* NATA_DMA || NATA_PIOBM */
|
2007-02-10 00:55:00 +03:00
|
|
|
aprint_verbose("\n");
|
2004-08-13 00:59:27 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-09-30 19:56:17 +04:00
|
|
|
#if NATA_DMA
|
2004-08-13 01:05:09 +04:00
|
|
|
/*
|
|
|
|
* downgrade the transfer mode of a drive after an error. return 1 if
|
|
|
|
* downgrade was possible, 0 otherwise.
|
2004-08-21 03:50:13 +04:00
|
|
|
*
|
|
|
|
* MUST BE CALLED AT splbio()!
|
2004-08-13 01:05:09 +04:00
|
|
|
*/
|
|
|
|
int
|
|
|
|
ata_downgrade_mode(struct ata_drive_datas *drvp, int flags)
|
|
|
|
{
|
2004-08-14 19:08:04 +04:00
|
|
|
struct ata_channel *chp = drvp->chnl_softc;
|
2004-08-20 10:39:37 +04:00
|
|
|
struct atac_softc *atac = chp->ch_atac;
|
2008-01-10 10:41:20 +03:00
|
|
|
device_t drv_dev = drvp->drv_softc;
|
2006-03-29 08:16:44 +04:00
|
|
|
int cf_flags = device_cfdata(drv_dev)->cf_flags;
|
2004-08-13 01:05:09 +04:00
|
|
|
|
|
|
|
/* if drive or controller don't know its mode, we can't do much */
|
|
|
|
if ((drvp->drive_flags & DRIVE_MODE) == 0 ||
|
2004-08-20 10:39:37 +04:00
|
|
|
(atac->atac_set_modes == NULL))
|
2004-08-13 01:05:09 +04:00
|
|
|
return 0;
|
|
|
|
/* current drive mode was set by a config flag, let it this way */
|
|
|
|
if ((cf_flags & ATA_CONFIG_PIO_SET) ||
|
|
|
|
(cf_flags & ATA_CONFIG_DMA_SET) ||
|
|
|
|
(cf_flags & ATA_CONFIG_UDMA_SET))
|
|
|
|
return 0;
|
|
|
|
|
2006-09-30 19:56:17 +04:00
|
|
|
#if NATA_UDMA
|
2004-08-13 01:05:09 +04:00
|
|
|
/*
|
|
|
|
* If we were using Ultra-DMA mode, downgrade to the next lower mode.
|
|
|
|
*/
|
|
|
|
if ((drvp->drive_flags & DRIVE_UDMA) && drvp->UDMA_mode >= 2) {
|
|
|
|
drvp->UDMA_mode--;
|
2008-03-18 23:46:35 +03:00
|
|
|
aprint_error_dev(drv_dev,
|
|
|
|
"transfer error, downgrading to Ultra-DMA mode %d\n",
|
|
|
|
drvp->UDMA_mode);
|
2004-08-13 01:05:09 +04:00
|
|
|
}
|
2006-09-30 19:56:17 +04:00
|
|
|
#endif
|
2004-08-13 01:05:09 +04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* If we were using ultra-DMA, don't downgrade to multiword DMA.
|
|
|
|
*/
|
|
|
|
else if (drvp->drive_flags & (DRIVE_DMA | DRIVE_UDMA)) {
|
|
|
|
drvp->drive_flags &= ~(DRIVE_DMA | DRIVE_UDMA);
|
|
|
|
drvp->PIO_mode = drvp->PIO_cap;
|
2008-03-18 23:46:35 +03:00
|
|
|
aprint_error_dev(drv_dev,
|
|
|
|
"transfer error, downgrading to PIO mode %d\n",
|
|
|
|
drvp->PIO_mode);
|
2004-08-13 01:05:09 +04:00
|
|
|
} else /* already using PIO, can't downgrade */
|
|
|
|
return 0;
|
|
|
|
|
2004-08-20 10:39:37 +04:00
|
|
|
(*atac->atac_set_modes)(chp);
|
2004-08-13 01:05:09 +04:00
|
|
|
ata_print_modes(chp);
|
2006-12-25 21:36:05 +03:00
|
|
|
/* reset the channel, which will schedule all drives for setup */
|
2004-08-21 03:26:53 +04:00
|
|
|
ata_reset_channel(chp, flags | AT_RST_NOCMD);
|
2004-08-13 01:05:09 +04:00
|
|
|
return 1;
|
|
|
|
}
|
2006-09-30 19:56:17 +04:00
|
|
|
#endif /* NATA_DMA */
|
2004-08-13 01:05:09 +04:00
|
|
|
|
2004-08-13 01:10:18 +04:00
|
|
|
/*
|
|
|
|
* Probe drive's capabilities, for use by the controller later
|
2005-02-27 03:26:58 +03:00
|
|
|
* Assumes drvp points to an existing drive.
|
2004-08-13 01:10:18 +04:00
|
|
|
*/
|
|
|
|
void
|
|
|
|
ata_probe_caps(struct ata_drive_datas *drvp)
|
|
|
|
{
|
|
|
|
struct ataparams params, params2;
|
2004-08-14 19:08:04 +04:00
|
|
|
struct ata_channel *chp = drvp->chnl_softc;
|
2004-08-20 10:39:37 +04:00
|
|
|
struct atac_softc *atac = chp->ch_atac;
|
2008-01-10 10:41:20 +03:00
|
|
|
device_t drv_dev = drvp->drv_softc;
|
2004-08-21 04:28:34 +04:00
|
|
|
int i, printed, s;
|
2005-05-30 02:11:28 +04:00
|
|
|
const char *sep = "";
|
2004-08-13 01:10:18 +04:00
|
|
|
int cf_flags;
|
|
|
|
|
|
|
|
if (ata_get_params(drvp, AT_WAIT, ¶ms) != CMD_OK) {
|
|
|
|
/* IDENTIFY failed. Can't tell more about the device */
|
|
|
|
return;
|
|
|
|
}
|
2004-08-20 10:39:37 +04:00
|
|
|
if ((atac->atac_cap & (ATAC_CAP_DATA16 | ATAC_CAP_DATA32)) ==
|
|
|
|
(ATAC_CAP_DATA16 | ATAC_CAP_DATA32)) {
|
2004-08-13 01:10:18 +04:00
|
|
|
/*
|
|
|
|
* Controller claims 16 and 32 bit transfers.
|
|
|
|
* Re-do an IDENTIFY with 32-bit transfers,
|
|
|
|
* and compare results.
|
|
|
|
*/
|
2004-08-21 04:28:34 +04:00
|
|
|
s = splbio();
|
2004-08-13 01:10:18 +04:00
|
|
|
drvp->drive_flags |= DRIVE_CAP32;
|
2004-08-21 04:28:34 +04:00
|
|
|
splx(s);
|
2004-08-13 01:10:18 +04:00
|
|
|
ata_get_params(drvp, AT_WAIT, ¶ms2);
|
|
|
|
if (memcmp(¶ms, ¶ms2, sizeof(struct ataparams)) != 0) {
|
|
|
|
/* Not good. fall back to 16bits */
|
2004-08-21 04:28:34 +04:00
|
|
|
s = splbio();
|
2004-08-13 01:10:18 +04:00
|
|
|
drvp->drive_flags &= ~DRIVE_CAP32;
|
2004-08-21 04:28:34 +04:00
|
|
|
splx(s);
|
2004-08-13 01:10:18 +04:00
|
|
|
} else {
|
2008-03-18 23:46:35 +03:00
|
|
|
aprint_verbose_dev(drv_dev, "32-bit data port\n");
|
2004-08-13 01:10:18 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
#if 0 /* Some ultra-DMA drives claims to only support ATA-3. sigh */
|
2005-02-27 03:26:58 +03:00
|
|
|
if (params.atap_ata_major > 0x01 &&
|
2004-08-13 01:10:18 +04:00
|
|
|
params.atap_ata_major != 0xffff) {
|
|
|
|
for (i = 14; i > 0; i--) {
|
|
|
|
if (params.atap_ata_major & (1 << i)) {
|
2008-03-18 23:46:35 +03:00
|
|
|
aprint_verbose_dev(drv_dev,
|
|
|
|
"ATA version %d\n", i);
|
2004-08-13 01:10:18 +04:00
|
|
|
drvp->ata_vers = i;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* An ATAPI device is at last PIO mode 3 */
|
|
|
|
if (drvp->drive_flags & DRIVE_ATAPI)
|
|
|
|
drvp->PIO_mode = 3;
|
|
|
|
|
|
|
|
/*
|
2005-02-27 03:26:58 +03:00
|
|
|
* It's not in the specs, but it seems that some drive
|
2004-08-13 01:10:18 +04:00
|
|
|
* returns 0xffff in atap_extensions when this field is invalid
|
|
|
|
*/
|
|
|
|
if (params.atap_extensions != 0xffff &&
|
|
|
|
(params.atap_extensions & WDC_EXT_MODES)) {
|
|
|
|
printed = 0;
|
|
|
|
/*
|
|
|
|
* XXX some drives report something wrong here (they claim to
|
|
|
|
* support PIO mode 8 !). As mode is coded on 3 bits in
|
|
|
|
* SET FEATURE, limit it to 7 (so limit i to 4).
|
|
|
|
* If higher mode than 7 is found, abort.
|
|
|
|
*/
|
|
|
|
for (i = 7; i >= 0; i--) {
|
|
|
|
if ((params.atap_piomode_supp & (1 << i)) == 0)
|
|
|
|
continue;
|
|
|
|
if (i > 4)
|
|
|
|
return;
|
|
|
|
/*
|
|
|
|
* See if mode is accepted.
|
|
|
|
* If the controller can't set its PIO mode,
|
|
|
|
* assume the defaults are good, so don't try
|
|
|
|
* to set it
|
|
|
|
*/
|
2004-08-20 10:39:37 +04:00
|
|
|
if (atac->atac_set_modes)
|
2004-08-13 01:10:18 +04:00
|
|
|
/*
|
|
|
|
* It's OK to pool here, it's fast enouth
|
|
|
|
* to not bother waiting for interrupt
|
|
|
|
*/
|
|
|
|
if (ata_set_mode(drvp, 0x08 | (i + 3),
|
|
|
|
AT_WAIT) != CMD_OK)
|
|
|
|
continue;
|
2005-02-27 03:26:58 +03:00
|
|
|
if (!printed) {
|
2008-03-18 23:46:35 +03:00
|
|
|
aprint_verbose_dev(drv_dev,
|
|
|
|
"drive supports PIO mode %d", i + 3);
|
2004-08-13 01:10:18 +04:00
|
|
|
sep = ",";
|
|
|
|
printed = 1;
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* If controller's driver can't set its PIO mode,
|
|
|
|
* get the highter one for the drive.
|
|
|
|
*/
|
2004-08-20 10:39:37 +04:00
|
|
|
if (atac->atac_set_modes == NULL ||
|
|
|
|
atac->atac_pio_cap >= i + 3) {
|
2004-08-13 01:10:18 +04:00
|
|
|
drvp->PIO_mode = i + 3;
|
|
|
|
drvp->PIO_cap = i + 3;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!printed) {
|
2005-02-27 03:26:58 +03:00
|
|
|
/*
|
2004-08-13 01:10:18 +04:00
|
|
|
* We didn't find a valid PIO mode.
|
|
|
|
* Assume the values returned for DMA are buggy too
|
|
|
|
*/
|
|
|
|
return;
|
|
|
|
}
|
2004-08-21 04:28:34 +04:00
|
|
|
s = splbio();
|
2004-08-13 01:10:18 +04:00
|
|
|
drvp->drive_flags |= DRIVE_MODE;
|
2004-08-21 04:28:34 +04:00
|
|
|
splx(s);
|
2004-08-13 01:10:18 +04:00
|
|
|
printed = 0;
|
|
|
|
for (i = 7; i >= 0; i--) {
|
|
|
|
if ((params.atap_dmamode_supp & (1 << i)) == 0)
|
|
|
|
continue;
|
2006-09-30 19:56:17 +04:00
|
|
|
#if NATA_DMA
|
2004-08-20 10:39:37 +04:00
|
|
|
if ((atac->atac_cap & ATAC_CAP_DMA) &&
|
|
|
|
atac->atac_set_modes != NULL)
|
2004-08-13 01:10:18 +04:00
|
|
|
if (ata_set_mode(drvp, 0x20 | i, AT_WAIT)
|
|
|
|
!= CMD_OK)
|
|
|
|
continue;
|
2006-09-30 19:56:17 +04:00
|
|
|
#endif
|
2004-08-13 01:10:18 +04:00
|
|
|
if (!printed) {
|
2007-02-10 00:55:00 +03:00
|
|
|
aprint_verbose("%s DMA mode %d", sep, i);
|
2004-08-13 01:10:18 +04:00
|
|
|
sep = ",";
|
|
|
|
printed = 1;
|
|
|
|
}
|
2006-09-30 19:56:17 +04:00
|
|
|
#if NATA_DMA
|
2004-08-20 10:39:37 +04:00
|
|
|
if (atac->atac_cap & ATAC_CAP_DMA) {
|
|
|
|
if (atac->atac_set_modes != NULL &&
|
|
|
|
atac->atac_dma_cap < i)
|
2004-08-13 01:10:18 +04:00
|
|
|
continue;
|
|
|
|
drvp->DMA_mode = i;
|
|
|
|
drvp->DMA_cap = i;
|
2004-08-21 04:28:34 +04:00
|
|
|
s = splbio();
|
2004-08-13 01:10:18 +04:00
|
|
|
drvp->drive_flags |= DRIVE_DMA;
|
2004-08-21 04:28:34 +04:00
|
|
|
splx(s);
|
2004-08-13 01:10:18 +04:00
|
|
|
}
|
2006-09-30 19:56:17 +04:00
|
|
|
#endif
|
2004-08-13 01:10:18 +04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (params.atap_extensions & WDC_EXT_UDMA_MODES) {
|
|
|
|
printed = 0;
|
|
|
|
for (i = 7; i >= 0; i--) {
|
|
|
|
if ((params.atap_udmamode_supp & (1 << i))
|
|
|
|
== 0)
|
|
|
|
continue;
|
2006-09-30 19:56:17 +04:00
|
|
|
#if NATA_UDMA
|
2004-08-20 10:39:37 +04:00
|
|
|
if (atac->atac_set_modes != NULL &&
|
|
|
|
(atac->atac_cap & ATAC_CAP_UDMA))
|
2004-08-13 01:10:18 +04:00
|
|
|
if (ata_set_mode(drvp, 0x40 | i,
|
|
|
|
AT_WAIT) != CMD_OK)
|
|
|
|
continue;
|
2006-09-30 19:56:17 +04:00
|
|
|
#endif
|
2004-08-13 01:10:18 +04:00
|
|
|
if (!printed) {
|
2007-02-10 00:55:00 +03:00
|
|
|
aprint_verbose("%s Ultra-DMA mode %d",
|
2004-08-13 01:10:18 +04:00
|
|
|
sep, i);
|
|
|
|
if (i == 2)
|
2007-02-10 00:55:00 +03:00
|
|
|
aprint_verbose(" (Ultra/33)");
|
2004-08-13 01:10:18 +04:00
|
|
|
else if (i == 4)
|
2007-02-10 00:55:00 +03:00
|
|
|
aprint_verbose(" (Ultra/66)");
|
2004-08-13 01:10:18 +04:00
|
|
|
else if (i == 5)
|
2007-02-10 00:55:00 +03:00
|
|
|
aprint_verbose(" (Ultra/100)");
|
2004-08-13 01:10:18 +04:00
|
|
|
else if (i == 6)
|
2007-02-10 00:55:00 +03:00
|
|
|
aprint_verbose(" (Ultra/133)");
|
2004-08-13 01:10:18 +04:00
|
|
|
sep = ",";
|
|
|
|
printed = 1;
|
|
|
|
}
|
2006-09-30 19:56:17 +04:00
|
|
|
#if NATA_UDMA
|
2004-08-20 10:39:37 +04:00
|
|
|
if (atac->atac_cap & ATAC_CAP_UDMA) {
|
|
|
|
if (atac->atac_set_modes != NULL &&
|
|
|
|
atac->atac_udma_cap < i)
|
2004-08-13 01:10:18 +04:00
|
|
|
continue;
|
|
|
|
drvp->UDMA_mode = i;
|
|
|
|
drvp->UDMA_cap = i;
|
2004-08-21 04:28:34 +04:00
|
|
|
s = splbio();
|
2004-08-13 01:10:18 +04:00
|
|
|
drvp->drive_flags |= DRIVE_UDMA;
|
2004-08-21 04:28:34 +04:00
|
|
|
splx(s);
|
2004-08-13 01:10:18 +04:00
|
|
|
}
|
2006-09-30 19:56:17 +04:00
|
|
|
#endif
|
2004-08-13 01:10:18 +04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2007-02-10 00:55:00 +03:00
|
|
|
aprint_verbose("\n");
|
2004-08-13 01:10:18 +04:00
|
|
|
}
|
|
|
|
|
2004-08-21 04:28:34 +04:00
|
|
|
s = splbio();
|
2004-08-13 01:10:18 +04:00
|
|
|
drvp->drive_flags &= ~DRIVE_NOSTREAM;
|
|
|
|
if (drvp->drive_flags & DRIVE_ATAPI) {
|
2005-02-27 03:26:58 +03:00
|
|
|
if (atac->atac_cap & ATAC_CAP_ATAPI_NOSTREAM)
|
2004-08-13 01:10:18 +04:00
|
|
|
drvp->drive_flags |= DRIVE_NOSTREAM;
|
|
|
|
} else {
|
2005-02-27 03:26:58 +03:00
|
|
|
if (atac->atac_cap & ATAC_CAP_ATA_NOSTREAM)
|
2004-08-13 01:10:18 +04:00
|
|
|
drvp->drive_flags |= DRIVE_NOSTREAM;
|
|
|
|
}
|
2004-08-21 04:28:34 +04:00
|
|
|
splx(s);
|
2004-08-13 01:10:18 +04:00
|
|
|
|
|
|
|
/* Try to guess ATA version here, if it didn't get reported */
|
|
|
|
if (drvp->ata_vers == 0) {
|
2006-09-30 19:56:17 +04:00
|
|
|
#if NATA_UDMA
|
2004-08-13 01:10:18 +04:00
|
|
|
if (drvp->drive_flags & DRIVE_UDMA)
|
|
|
|
drvp->ata_vers = 4; /* should be at last ATA-4 */
|
2006-09-30 19:56:17 +04:00
|
|
|
else
|
|
|
|
#endif
|
|
|
|
if (drvp->PIO_cap > 2)
|
2004-08-13 01:10:18 +04:00
|
|
|
drvp->ata_vers = 2; /* should be at last ATA-2 */
|
|
|
|
}
|
2006-03-29 08:16:44 +04:00
|
|
|
cf_flags = device_cfdata(drv_dev)->cf_flags;
|
2004-08-13 01:10:18 +04:00
|
|
|
if (cf_flags & ATA_CONFIG_PIO_SET) {
|
2004-08-21 04:28:34 +04:00
|
|
|
s = splbio();
|
2004-08-13 01:10:18 +04:00
|
|
|
drvp->PIO_mode =
|
|
|
|
(cf_flags & ATA_CONFIG_PIO_MODES) >> ATA_CONFIG_PIO_OFF;
|
|
|
|
drvp->drive_flags |= DRIVE_MODE;
|
2004-08-21 04:28:34 +04:00
|
|
|
splx(s);
|
2004-08-13 01:10:18 +04:00
|
|
|
}
|
2006-09-30 19:56:17 +04:00
|
|
|
#if NATA_DMA
|
2004-08-20 10:39:37 +04:00
|
|
|
if ((atac->atac_cap & ATAC_CAP_DMA) == 0) {
|
2004-08-13 01:10:18 +04:00
|
|
|
/* don't care about DMA modes */
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (cf_flags & ATA_CONFIG_DMA_SET) {
|
2004-08-21 04:28:34 +04:00
|
|
|
s = splbio();
|
2004-08-13 01:10:18 +04:00
|
|
|
if ((cf_flags & ATA_CONFIG_DMA_MODES) ==
|
|
|
|
ATA_CONFIG_DMA_DISABLE) {
|
|
|
|
drvp->drive_flags &= ~DRIVE_DMA;
|
|
|
|
} else {
|
|
|
|
drvp->DMA_mode = (cf_flags & ATA_CONFIG_DMA_MODES) >>
|
|
|
|
ATA_CONFIG_DMA_OFF;
|
|
|
|
drvp->drive_flags |= DRIVE_DMA | DRIVE_MODE;
|
|
|
|
}
|
2004-08-21 04:28:34 +04:00
|
|
|
splx(s);
|
2004-08-13 01:10:18 +04:00
|
|
|
}
|
2006-09-30 19:56:17 +04:00
|
|
|
#if NATA_UDMA
|
2004-08-20 10:39:37 +04:00
|
|
|
if ((atac->atac_cap & ATAC_CAP_UDMA) == 0) {
|
2004-08-13 01:10:18 +04:00
|
|
|
/* don't care about UDMA modes */
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (cf_flags & ATA_CONFIG_UDMA_SET) {
|
2004-08-21 04:28:34 +04:00
|
|
|
s = splbio();
|
2004-08-13 01:10:18 +04:00
|
|
|
if ((cf_flags & ATA_CONFIG_UDMA_MODES) ==
|
|
|
|
ATA_CONFIG_UDMA_DISABLE) {
|
|
|
|
drvp->drive_flags &= ~DRIVE_UDMA;
|
|
|
|
} else {
|
|
|
|
drvp->UDMA_mode = (cf_flags & ATA_CONFIG_UDMA_MODES) >>
|
|
|
|
ATA_CONFIG_UDMA_OFF;
|
|
|
|
drvp->drive_flags |= DRIVE_UDMA | DRIVE_MODE;
|
|
|
|
}
|
2004-08-21 04:28:34 +04:00
|
|
|
splx(s);
|
2004-08-13 01:10:18 +04:00
|
|
|
}
|
2006-09-30 19:56:17 +04:00
|
|
|
#endif /* NATA_UDMA */
|
|
|
|
#endif /* NATA_DMA */
|
2004-08-13 01:10:18 +04:00
|
|
|
}
|
|
|
|
|
2004-08-02 01:40:41 +04:00
|
|
|
/* management of the /dev/atabus* devices */
|
2004-08-21 02:26:23 +04:00
|
|
|
int
|
2006-11-16 04:32:37 +03:00
|
|
|
atabusopen(dev_t dev, int flag, int fmt,
|
|
|
|
struct lwp *l)
|
2004-08-02 01:40:41 +04:00
|
|
|
{
|
2006-10-15 04:00:00 +04:00
|
|
|
struct atabus_softc *sc;
|
2008-06-13 01:46:21 +04:00
|
|
|
int error;
|
2005-02-27 03:26:58 +03:00
|
|
|
|
2008-06-13 01:46:21 +04:00
|
|
|
sc = device_lookup_private(&atabus_cd, minor(dev));
|
|
|
|
if (sc == NULL)
|
2006-10-15 04:00:00 +04:00
|
|
|
return (ENXIO);
|
2005-02-27 03:26:58 +03:00
|
|
|
|
2006-10-15 04:00:00 +04:00
|
|
|
if (sc->sc_flags & ATABUSCF_OPEN)
|
|
|
|
return (EBUSY);
|
2004-08-02 01:40:41 +04:00
|
|
|
|
2006-10-15 04:00:00 +04:00
|
|
|
if ((error = ata_addref(sc->sc_chan)) != 0)
|
|
|
|
return (error);
|
2004-08-02 01:40:41 +04:00
|
|
|
|
2006-10-15 04:00:00 +04:00
|
|
|
sc->sc_flags |= ATABUSCF_OPEN;
|
2004-08-02 01:40:41 +04:00
|
|
|
|
2006-10-15 04:00:00 +04:00
|
|
|
return (0);
|
2004-08-02 01:40:41 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
2006-11-16 04:32:37 +03:00
|
|
|
atabusclose(dev_t dev, int flag, int fmt,
|
|
|
|
struct lwp *l)
|
2004-08-02 01:40:41 +04:00
|
|
|
{
|
2008-03-18 23:46:35 +03:00
|
|
|
struct atabus_softc *sc =
|
2008-06-13 01:46:21 +04:00
|
|
|
device_lookup_private(&atabus_cd, minor(dev));
|
2004-08-02 01:40:41 +04:00
|
|
|
|
2006-10-15 04:00:00 +04:00
|
|
|
ata_delref(sc->sc_chan);
|
2004-08-02 01:40:41 +04:00
|
|
|
|
2006-10-15 04:00:00 +04:00
|
|
|
sc->sc_flags &= ~ATABUSCF_OPEN;
|
2004-08-02 01:40:41 +04:00
|
|
|
|
2006-10-15 04:00:00 +04:00
|
|
|
return (0);
|
2004-08-02 01:40:41 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
int
|
2007-03-04 08:59:00 +03:00
|
|
|
atabusioctl(dev_t dev, u_long cmd, void *addr, int flag,
|
2006-11-16 04:32:37 +03:00
|
|
|
struct lwp *l)
|
2004-08-02 01:40:41 +04:00
|
|
|
{
|
2008-03-18 23:46:35 +03:00
|
|
|
struct atabus_softc *sc =
|
2008-06-13 01:46:21 +04:00
|
|
|
device_lookup_private(&atabus_cd, minor(dev));
|
2004-08-14 19:08:04 +04:00
|
|
|
struct ata_channel *chp = sc->sc_chan;
|
2004-08-04 02:37:19 +04:00
|
|
|
int min_drive, max_drive, drive;
|
2006-10-15 04:00:00 +04:00
|
|
|
int error;
|
2004-08-02 01:40:41 +04:00
|
|
|
int s;
|
|
|
|
|
2006-10-15 04:00:00 +04:00
|
|
|
/*
|
|
|
|
* Enforce write permission for ioctls that change the
|
|
|
|
* state of the bus. Host adapter specific ioctls must
|
|
|
|
* be checked by the adapter driver.
|
|
|
|
*/
|
|
|
|
switch (cmd) {
|
|
|
|
case ATABUSIOSCAN:
|
|
|
|
case ATABUSIODETACH:
|
|
|
|
case ATABUSIORESET:
|
|
|
|
if ((flag & FWRITE) == 0)
|
|
|
|
return (EBADF);
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (cmd) {
|
|
|
|
case ATABUSIORESET:
|
2004-08-02 01:40:41 +04:00
|
|
|
s = splbio();
|
2004-08-21 03:26:53 +04:00
|
|
|
ata_reset_channel(sc->sc_chan, AT_WAIT | AT_POLL);
|
2004-08-02 01:40:41 +04:00
|
|
|
splx(s);
|
|
|
|
error = 0;
|
|
|
|
break;
|
2004-08-04 02:37:19 +04:00
|
|
|
case ATABUSIOSCAN:
|
|
|
|
{
|
|
|
|
#if 0
|
|
|
|
struct atabusioscan_args *a=
|
|
|
|
(struct atabusioscan_args *)addr;
|
|
|
|
#endif
|
|
|
|
if ((chp->ch_drive[0].drive_flags & DRIVE_OLD) ||
|
|
|
|
(chp->ch_drive[1].drive_flags & DRIVE_OLD))
|
|
|
|
return (EOPNOTSUPP);
|
|
|
|
return (EOPNOTSUPP);
|
|
|
|
}
|
|
|
|
case ATABUSIODETACH:
|
|
|
|
{
|
|
|
|
struct atabusioscan_args *a=
|
|
|
|
(struct atabusioscan_args *)addr;
|
|
|
|
if ((chp->ch_drive[0].drive_flags & DRIVE_OLD) ||
|
|
|
|
(chp->ch_drive[1].drive_flags & DRIVE_OLD))
|
|
|
|
return (EOPNOTSUPP);
|
|
|
|
switch (a->at_dev) {
|
|
|
|
case -1:
|
|
|
|
min_drive = 0;
|
|
|
|
max_drive = 1;
|
|
|
|
break;
|
|
|
|
case 0:
|
|
|
|
case 1:
|
|
|
|
min_drive = max_drive = a->at_dev;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return (EINVAL);
|
|
|
|
}
|
|
|
|
for (drive = min_drive; drive <= max_drive; drive++) {
|
|
|
|
if (chp->ch_drive[drive].drv_softc != NULL) {
|
|
|
|
error = config_detach(
|
|
|
|
chp->ch_drive[drive].drv_softc, 0);
|
|
|
|
if (error)
|
|
|
|
return (error);
|
2008-01-10 10:41:20 +03:00
|
|
|
KASSERT(chp->ch_drive[drive].drv_softc == NULL);
|
2004-08-04 02:37:19 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
error = 0;
|
|
|
|
break;
|
|
|
|
}
|
2004-08-02 01:40:41 +04:00
|
|
|
default:
|
|
|
|
error = ENOTTY;
|
|
|
|
}
|
|
|
|
return (error);
|
|
|
|
};
|
2005-01-27 00:51:40 +03:00
|
|
|
|
2007-12-09 23:27:42 +03:00
|
|
|
static bool
|
2008-02-29 09:38:28 +03:00
|
|
|
atabus_suspend(device_t dv PMF_FN_ARGS)
|
2005-01-27 00:51:40 +03:00
|
|
|
{
|
2007-12-09 23:27:42 +03:00
|
|
|
struct atabus_softc *sc = device_private(dv);
|
|
|
|
struct ata_channel *chp = sc->sc_chan;
|
|
|
|
|
|
|
|
ata_queue_idle(chp->ch_queue);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool
|
2008-02-29 09:38:28 +03:00
|
|
|
atabus_resume(device_t dv PMF_FN_ARGS)
|
2007-12-09 23:27:42 +03:00
|
|
|
{
|
|
|
|
struct atabus_softc *sc = device_private(dv);
|
2005-01-27 00:51:40 +03:00
|
|
|
struct ata_channel *chp = sc->sc_chan;
|
|
|
|
int s;
|
|
|
|
|
2007-12-09 23:27:42 +03:00
|
|
|
/*
|
|
|
|
* XXX joerg: with wdc, the first channel unfreezes the controler.
|
|
|
|
* Move this the reset and queue idling into wdc.
|
|
|
|
*/
|
|
|
|
s = splbio();
|
|
|
|
if (chp->ch_queue->queue_freeze == 0) {
|
2005-05-17 01:43:33 +04:00
|
|
|
splx(s);
|
2007-12-09 23:27:42 +03:00
|
|
|
return true;
|
2005-01-27 00:51:40 +03:00
|
|
|
}
|
2007-12-09 23:27:42 +03:00
|
|
|
KASSERT(chp->ch_queue->queue_freeze > 0);
|
|
|
|
/* unfreeze the queue and reset drives */
|
|
|
|
chp->ch_queue->queue_freeze--;
|
|
|
|
ata_reset_channel(chp, AT_WAIT);
|
|
|
|
splx(s);
|
2005-01-27 00:51:40 +03:00
|
|
|
|
2007-12-09 23:27:42 +03:00
|
|
|
return true;
|
2005-01-27 00:51:40 +03:00
|
|
|
}
|