version 1.141, 2017/10/28 04:53:54 |
version 1.141.4.2, 2020/04/08 14:08:03 |
Line 32 __KERNEL_RCSID(0, "$NetBSD$"); |
|
Line 32 __KERNEL_RCSID(0, "$NetBSD$"); |
|
#include <sys/param.h> |
#include <sys/param.h> |
#include <sys/systm.h> |
#include <sys/systm.h> |
#include <sys/kernel.h> |
#include <sys/kernel.h> |
#include <sys/malloc.h> |
|
#include <sys/device.h> |
#include <sys/device.h> |
#include <sys/conf.h> |
#include <sys/conf.h> |
#include <sys/fcntl.h> |
#include <sys/fcntl.h> |
Line 45 __KERNEL_RCSID(0, "$NetBSD$"); |
|
Line 44 __KERNEL_RCSID(0, "$NetBSD$"); |
|
#include <sys/bus.h> |
#include <sys/bus.h> |
#include <sys/once.h> |
#include <sys/once.h> |
#include <sys/bitops.h> |
#include <sys/bitops.h> |
|
#include <sys/cpu.h> |
|
|
#define ATABUS_PRIVATE |
#define ATABUS_PRIVATE |
|
|
Line 85 int atadebug_mask = ATADEBUG_MASK; |
|
Line 85 int atadebug_mask = ATADEBUG_MASK; |
|
#endif |
#endif |
|
|
static ONCE_DECL(ata_init_ctrl); |
static ONCE_DECL(ata_init_ctrl); |
|
static struct pool ata_xfer_pool; |
|
|
/* |
/* |
* A queue of atabus instances, used to ensure the same bus probe order |
* A queue of atabus instances, used to ensure the same bus probe order |
Line 129 static bool atabus_suspend(device_t, con |
|
Line 130 static bool atabus_suspend(device_t, con |
|
static void atabusconfig_thread(void *); |
static void atabusconfig_thread(void *); |
|
|
static void ata_channel_idle(struct ata_channel *); |
static void ata_channel_idle(struct ata_channel *); |
static void ata_channel_thaw_locked(struct ata_channel *); |
|
static void ata_activate_xfer_locked(struct ata_channel *, struct ata_xfer *); |
static void ata_activate_xfer_locked(struct ata_channel *, struct ata_xfer *); |
static void ata_channel_freeze_locked(struct ata_channel *); |
static void ata_channel_freeze_locked(struct ata_channel *); |
static void ata_thread_wake_locked(struct ata_channel *); |
static void ata_thread_wake_locked(struct ata_channel *); |
|
|
atabus_init(void) |
atabus_init(void) |
{ |
{ |
|
|
|
pool_init(&ata_xfer_pool, sizeof(struct ata_xfer), 0, 0, 0, |
|
"ataspl", NULL, IPL_BIO); |
TAILQ_INIT(&atabus_initq_head); |
TAILQ_INIT(&atabus_initq_head); |
mutex_init(&atabus_qlock, MUTEX_DEFAULT, IPL_NONE); |
mutex_init(&atabus_qlock, MUTEX_DEFAULT, IPL_NONE); |
cv_init(&atabus_qcv, "atainitq"); |
cv_init(&atabus_qcv, "atainitq"); |
Line 215 ata_channel_detach(struct ata_channel *c |
|
Line 217 ata_channel_detach(struct ata_channel *c |
|
return; |
return; |
|
|
ata_channel_destroy(chp); |
ata_channel_destroy(chp); |
|
|
|
chp->ch_flags |= ATACH_DETACHED; |
} |
} |
|
|
static void |
static void |
Line 226 atabusconfig(struct atabus_softc *atabus |
|
Line 230 atabusconfig(struct atabus_softc *atabus |
|
int i, error; |
int i, error; |
|
|
/* we are in the atabus's thread context */ |
/* we are in the atabus's thread context */ |
ata_channel_lock(chp); |
|
chp->ch_flags |= ATACH_TH_RUN; |
|
ata_channel_unlock(chp); |
|
|
|
/* |
/* |
* Probe for the drives attached to controller, unless a PMP |
* Probe for the drives attached to controller, unless a PMP |
Line 244 atabusconfig(struct atabus_softc *atabus |
|
Line 245 atabusconfig(struct atabus_softc *atabus |
|
DEBUG_PROBE); |
DEBUG_PROBE); |
} |
} |
|
|
/* next operations will occurs in a separate thread */ |
|
ata_channel_lock(chp); |
|
chp->ch_flags &= ~ATACH_TH_RUN; |
|
ata_channel_unlock(chp); |
|
|
|
/* Make sure the devices probe in atabus order to avoid jitter. */ |
/* Make sure the devices probe in atabus order to avoid jitter. */ |
mutex_enter(&atabus_qlock); |
mutex_enter(&atabus_qlock); |
for (;;) { |
for (;;) { |
Line 261 atabusconfig(struct atabus_softc *atabus |
|
Line 257 atabusconfig(struct atabus_softc *atabus |
|
|
|
ata_channel_lock(chp); |
ata_channel_lock(chp); |
|
|
|
KASSERT(ata_is_thread_run(chp)); |
|
|
/* If no drives, abort here */ |
/* If no drives, abort here */ |
if (chp->ch_drive == NULL) |
if (chp->ch_drive == NULL) |
goto out; |
goto out; |
Line 292 atabusconfig(struct atabus_softc *atabus |
|
Line 290 atabusconfig(struct atabus_softc *atabus |
|
cv_broadcast(&atabus_qcv); |
cv_broadcast(&atabus_qcv); |
mutex_exit(&atabus_qlock); |
mutex_exit(&atabus_qlock); |
|
|
free(atabus_initq, M_DEVBUF); |
kmem_free(atabus_initq, sizeof(*atabus_initq)); |
|
|
ata_delref(chp); |
ata_delref(chp); |
|
|
Line 418 atabusconfig_thread(void *arg) |
|
Line 416 atabusconfig_thread(void *arg) |
|
cv_broadcast(&atabus_qcv); |
cv_broadcast(&atabus_qcv); |
mutex_exit(&atabus_qlock); |
mutex_exit(&atabus_qlock); |
|
|
free(atabus_initq, M_DEVBUF); |
kmem_free(atabus_initq, sizeof(*atabus_initq)); |
|
|
ata_delref(chp); |
ata_delref(chp); |
|
|
Line 438 atabus_thread(void *arg) |
|
Line 436 atabus_thread(void *arg) |
|
struct ata_channel *chp = sc->sc_chan; |
struct ata_channel *chp = sc->sc_chan; |
struct ata_queue *chq = chp->ch_queue; |
struct ata_queue *chq = chp->ch_queue; |
struct ata_xfer *xfer; |
struct ata_xfer *xfer; |
int i, rv, s; |
int i, rv; |
|
|
ata_channel_lock(chp); |
ata_channel_lock(chp); |
chp->ch_flags |= ATACH_TH_RUN; |
KASSERT(ata_is_thread_run(chp)); |
|
|
/* |
/* |
* Probe the drives. Reset type to indicate to controllers |
* Probe the drives. Reset type to indicate to controllers |
Line 460 atabus_thread(void *arg) |
|
Line 458 atabus_thread(void *arg) |
|
|
|
ata_channel_lock(chp); |
ata_channel_lock(chp); |
for (;;) { |
for (;;) { |
if ((chp->ch_flags & (ATACH_TH_RESET | ATACH_SHUTDOWN)) == 0 && |
if ((chp->ch_flags & (ATACH_TH_RESET | ATACH_TH_DRIVE_RESET |
|
| ATACH_TH_RECOVERY | ATACH_SHUTDOWN)) == 0 && |
(chq->queue_active == 0 || chq->queue_freeze == 0)) { |
(chq->queue_active == 0 || chq->queue_freeze == 0)) { |
chp->ch_flags &= ~ATACH_TH_RUN; |
|
cv_wait(&chp->ch_thr_idle, &chp->ch_lock); |
cv_wait(&chp->ch_thr_idle, &chp->ch_lock); |
chp->ch_flags |= ATACH_TH_RUN; |
|
} |
} |
if (chp->ch_flags & ATACH_SHUTDOWN) { |
if (chp->ch_flags & ATACH_SHUTDOWN) { |
break; |
break; |
Line 476 atabus_thread(void *arg) |
|
Line 473 atabus_thread(void *arg) |
|
ata_channel_lock(chp); |
ata_channel_lock(chp); |
} |
} |
if (chp->ch_flags & ATACH_TH_RESET) { |
if (chp->ch_flags & ATACH_TH_RESET) { |
/* ata_reset_channel() will unfreeze the channel */ |
/* this will unfreeze the channel */ |
ata_channel_unlock(chp); |
ata_thread_run(chp, AT_WAIT, |
s = splbio(); |
ATACH_TH_RESET, ATACH_NODRIVE); |
ata_reset_channel(chp, AT_WAIT | chp->ch_reset_flags); |
} else if (chp->ch_flags & ATACH_TH_DRIVE_RESET) { |
|
/* this will unfreeze the channel */ |
|
for (i = 0; i < chp->ch_ndrives; i++) { |
|
struct ata_drive_datas *drvp; |
|
|
|
drvp = &chp->ch_drive[i]; |
|
|
|
if (drvp->drive_flags & ATA_DRIVE_TH_RESET) { |
|
ata_thread_run(chp, |
|
AT_WAIT, ATACH_TH_DRIVE_RESET, i); |
|
} |
|
} |
|
chp->ch_flags &= ~ATACH_TH_DRIVE_RESET; |
|
} else if (chp->ch_flags & ATACH_TH_RECOVERY) { |
|
/* |
|
* This will unfreeze the channel; drops locks during |
|
* run, so must wrap in splbio()/splx() to avoid |
|
* spurious interrupts. XXX MPSAFE |
|
*/ |
|
int s = splbio(); |
|
ata_thread_run(chp, AT_WAIT, ATACH_TH_RECOVERY, |
|
chp->recovery_tfd); |
splx(s); |
splx(s); |
ata_channel_lock(chp); |
|
} else if (chq->queue_active > 0 && chq->queue_freeze == 1) { |
} else if (chq->queue_active > 0 && chq->queue_freeze == 1) { |
/* |
/* |
* Caller has bumped queue_freeze, decrease it. This |
* Caller has bumped queue_freeze, decrease it. This |
Line 509 atabus_thread(void *arg) |
|
Line 526 atabus_thread(void *arg) |
|
} |
} |
} else if (chq->queue_freeze > 1) |
} else if (chq->queue_freeze > 1) |
panic("%s: queue_freeze", __func__); |
panic("%s: queue_freeze", __func__); |
|
|
|
/* Try to run down the queue once channel is unfrozen */ |
|
if (chq->queue_freeze == 0) { |
|
ata_channel_unlock(chp); |
|
atastart(chp); |
|
ata_channel_lock(chp); |
|
} |
} |
} |
chp->ch_thread = NULL; |
chp->ch_thread = NULL; |
cv_signal(&chp->ch_thr_idle); |
cv_signal(&chp->ch_thr_idle); |
Line 516 atabus_thread(void *arg) |
|
Line 540 atabus_thread(void *arg) |
|
kthread_exit(0); |
kthread_exit(0); |
} |
} |
|
|
|
bool |
|
ata_is_thread_run(struct ata_channel *chp) |
|
{ |
|
KASSERT(mutex_owned(&chp->ch_lock)); |
|
|
|
return (chp->ch_thread == curlwp && !cpu_intr_p()); |
|
} |
|
|
static void |
static void |
ata_thread_wake_locked(struct ata_channel *chp) |
ata_thread_wake_locked(struct ata_channel *chp) |
{ |
{ |
Line 569 atabus_attach(device_t parent, device_t |
|
Line 601 atabus_attach(device_t parent, device_t |
|
|
|
RUN_ONCE(&ata_init_ctrl, atabus_init); |
RUN_ONCE(&ata_init_ctrl, atabus_init); |
|
|
initq = malloc(sizeof(*initq), M_DEVBUF, M_WAITOK); |
initq = kmem_zalloc(sizeof(*initq), KM_SLEEP); |
initq->atabus_sc = sc; |
initq->atabus_sc = sc; |
mutex_enter(&atabus_qlock); |
mutex_enter(&atabus_qlock); |
TAILQ_INSERT_TAIL(&atabus_initq_head, initq, atabus_initq); |
TAILQ_INSERT_TAIL(&atabus_initq_head, initq, atabus_initq); |
mutex_exit(&atabus_qlock); |
mutex_exit(&atabus_qlock); |
config_pending_incr(sc->sc_dev); |
config_pending_incr(sc->sc_dev); |
|
|
|
/* XXX MPSAFE - no KTHREAD_MPSAFE, so protected by KERNEL_LOCK() */ |
if ((error = kthread_create(PRI_NONE, 0, NULL, atabus_thread, sc, |
if ((error = kthread_create(PRI_NONE, 0, NULL, atabus_thread, sc, |
&chp->ch_thread, "%s", device_xname(self))) != 0) |
&chp->ch_thread, "%s", device_xname(self))) != 0) |
aprint_error_dev(self, |
aprint_error_dev(self, |
Line 598 atabus_detach(device_t self, int flags) |
|
Line 631 atabus_detach(device_t self, int flags) |
|
device_t dev = NULL; |
device_t dev = NULL; |
int i, error = 0; |
int i, error = 0; |
|
|
/* Shutdown the channel. */ |
|
ata_channel_lock(chp); |
|
chp->ch_flags |= ATACH_SHUTDOWN; |
|
while (chp->ch_thread != NULL) { |
|
cv_signal(&chp->ch_thr_idle); |
|
cv_wait(&chp->ch_thr_idle, &chp->ch_lock); |
|
} |
|
ata_channel_unlock(chp); |
|
|
|
/* |
/* |
* Detach atapibus and its children. |
* Detach atapibus and its children. |
*/ |
*/ |
Line 641 atabus_detach(device_t self, int flags) |
|
Line 665 atabus_detach(device_t self, int flags) |
|
KASSERT(chp->ch_drive[i].drive_type == 0); |
KASSERT(chp->ch_drive[i].drive_type == 0); |
} |
} |
} |
} |
|
|
|
/* Shutdown the channel. */ |
|
ata_channel_lock(chp); |
|
chp->ch_flags |= ATACH_SHUTDOWN; |
|
while (chp->ch_thread != NULL) { |
|
cv_signal(&chp->ch_thr_idle); |
|
cv_wait(&chp->ch_thr_idle, &chp->ch_lock); |
|
} |
|
ata_channel_unlock(chp); |
|
|
atabus_free_drives(chp); |
atabus_free_drives(chp); |
|
|
out: |
out: |
Line 716 atabus_alloc_drives(struct ata_channel * |
|
Line 750 atabus_alloc_drives(struct ata_channel * |
|
if (chp->ch_ndrives != ndrives) |
if (chp->ch_ndrives != ndrives) |
atabus_free_drives(chp); |
atabus_free_drives(chp); |
if (chp->ch_drive == NULL) { |
if (chp->ch_drive == NULL) { |
chp->ch_drive = malloc( |
chp->ch_drive = kmem_zalloc( |
sizeof(struct ata_drive_datas) * ndrives, |
sizeof(struct ata_drive_datas) * ndrives, KM_NOSLEEP); |
M_DEVBUF, M_NOWAIT | M_ZERO); |
|
} |
} |
if (chp->ch_drive == NULL) { |
if (chp->ch_drive == NULL) { |
aprint_error_dev(chp->ch_atac->atac_dev, |
aprint_error_dev(chp->ch_atac->atac_dev, |
Line 761 atabus_free_drives(struct ata_channel *c |
|
Line 794 atabus_free_drives(struct ata_channel *c |
|
|
|
if (chp->ch_drive == NULL) |
if (chp->ch_drive == NULL) |
return; |
return; |
|
kmem_free(chp->ch_drive, |
|
sizeof(struct ata_drive_datas) * chp->ch_ndrives); |
chp->ch_ndrives = 0; |
chp->ch_ndrives = 0; |
free(chp->ch_drive, M_DEVBUF); |
|
chp->ch_drive = NULL; |
chp->ch_drive = NULL; |
} |
} |
|
|
Line 780 ata_get_params(struct ata_drive_datas *d |
|
Line 814 ata_get_params(struct ata_drive_datas *d |
|
|
|
ATADEBUG_PRINT(("%s\n", __func__), DEBUG_FUNCS); |
ATADEBUG_PRINT(("%s\n", __func__), DEBUG_FUNCS); |
|
|
xfer = ata_get_xfer(chp); |
xfer = ata_get_xfer(chp, false); |
if (xfer == NULL) { |
if (xfer == NULL) { |
ATADEBUG_PRINT(("%s: no xfer\n", __func__), |
ATADEBUG_PRINT(("%s: no xfer\n", __func__), |
DEBUG_FUNCS|DEBUG_PROBE); |
DEBUG_FUNCS|DEBUG_PROBE); |
Line 885 ata_set_mode(struct ata_drive_datas *drv |
|
Line 919 ata_set_mode(struct ata_drive_datas *drv |
|
|
|
ATADEBUG_PRINT(("ata_set_mode=0x%x\n", mode), DEBUG_FUNCS); |
ATADEBUG_PRINT(("ata_set_mode=0x%x\n", mode), DEBUG_FUNCS); |
|
|
xfer = ata_get_xfer(chp); |
xfer = ata_get_xfer(chp, false); |
if (xfer == NULL) { |
if (xfer == NULL) { |
ATADEBUG_PRINT(("%s: no xfer\n", __func__), |
ATADEBUG_PRINT(("%s: no xfer\n", __func__), |
DEBUG_FUNCS|DEBUG_PROBE); |
DEBUG_FUNCS|DEBUG_PROBE); |
|
|
return rv; |
return rv; |
} |
} |
|
|
int |
|
ata_read_log_ext_ncq(struct ata_drive_datas *drvp, uint8_t flags, |
|
uint8_t *slot, uint8_t *status, uint8_t *err) |
|
{ |
|
struct ata_xfer *xfer; |
|
int rv; |
|
struct ata_channel *chp = drvp->chnl_softc; |
|
struct atac_softc *atac = chp->ch_atac; |
|
uint8_t *tb, cksum, page; |
|
|
|
ATADEBUG_PRINT(("%s\n", __func__), DEBUG_FUNCS); |
|
|
|
/* Only NCQ ATA drives support/need this */ |
|
if (drvp->drive_type != ATA_DRIVET_ATA || |
|
(drvp->drive_flags & ATA_DRIVE_NCQ) == 0) |
|
return EOPNOTSUPP; |
|
|
|
xfer = ata_get_xfer_ext(chp, C_RECOVERY, 0); |
|
|
|
tb = drvp->recovery_blk; |
|
memset(tb, 0, sizeof(drvp->recovery_blk)); |
|
|
|
/* |
|
* We could use READ LOG DMA EXT if drive supports it (i.e. |
|
* when it supports Streaming feature) to avoid PIO command, |
|
* and to make this a little faster. Realistically, it |
|
* should not matter. |
|
*/ |
|
xfer->c_flags |= C_RECOVERY; |
|
xfer->c_ata_c.r_command = WDCC_READ_LOG_EXT; |
|
xfer->c_ata_c.r_lba = page = WDCC_LOG_PAGE_NCQ; |
|
xfer->c_ata_c.r_st_bmask = WDCS_DRDY; |
|
xfer->c_ata_c.r_st_pmask = WDCS_DRDY; |
|
xfer->c_ata_c.r_count = 1; |
|
xfer->c_ata_c.r_device = WDSD_LBA; |
|
xfer->c_ata_c.flags = AT_READ | AT_LBA | AT_LBA48 | flags; |
|
xfer->c_ata_c.timeout = 1000; /* 1s */ |
|
xfer->c_ata_c.data = tb; |
|
xfer->c_ata_c.bcount = sizeof(drvp->recovery_blk); |
|
|
|
if ((*atac->atac_bustype_ata->ata_exec_command)(drvp, |
|
xfer) != ATACMD_COMPLETE) { |
|
rv = EAGAIN; |
|
goto out; |
|
} |
|
if (xfer->c_ata_c.flags & (AT_ERROR | AT_TIMEOU | AT_DF)) { |
|
rv = EINVAL; |
|
goto out; |
|
} |
|
|
|
cksum = 0; |
|
for (int i = 0; i < sizeof(drvp->recovery_blk); i++) |
|
cksum += tb[i]; |
|
if (cksum != 0) { |
|
aprint_error_dev(drvp->drv_softc, |
|
"invalid checksum %x for READ LOG EXT page %x\n", |
|
cksum, page); |
|
rv = EINVAL; |
|
goto out; |
|
} |
|
|
|
if (tb[0] & WDCC_LOG_NQ) { |
|
/* not queued command */ |
|
rv = EOPNOTSUPP; |
|
goto out; |
|
} |
|
|
|
*slot = tb[0] & 0x1f; |
|
*status = tb[2]; |
|
*err = tb[3]; |
|
|
|
KASSERTMSG((*status & WDCS_ERR), |
|
"%s: non-error command slot %d reported by READ LOG EXT page %x: " |
|
"err %x status %x\n", |
|
device_xname(drvp->drv_softc), *slot, page, *err, *status); |
|
|
|
rv = 0; |
|
|
|
out: |
|
ata_free_xfer(chp, xfer); |
|
return rv; |
|
} |
|
|
|
#if NATA_DMA |
#if NATA_DMA |
void |
void |
ata_dmaerr(struct ata_drive_datas *drvp, int flags) |
ata_dmaerr(struct ata_drive_datas *drvp, int flags) |
{ |
{ |
|
ata_channel_lock_owned(drvp->chnl_softc); |
|
|
/* |
/* |
* Downgrade decision: if we get NERRS_MAX in NXFER. |
* Downgrade decision: if we get NERRS_MAX in NXFER. |
* We start with n_dmaerrs set to NERRS_MAX-1 so that the |
* We start with n_dmaerrs set to NERRS_MAX-1 so that the |
Line 1061 ata_exec_xfer(struct ata_channel *chp, s |
|
Line 1014 ata_exec_xfer(struct ata_channel *chp, s |
|
* Standard commands are added to the end of command list, but |
* Standard commands are added to the end of command list, but |
* recovery commands must be run immediatelly. |
* recovery commands must be run immediatelly. |
*/ |
*/ |
if ((xfer->c_flags & C_RECOVERY) == 0) |
if ((xfer->c_flags & C_SKIP_QUEUE) == 0) |
TAILQ_INSERT_TAIL(&chp->ch_queue->queue_xfer, xfer, |
SIMPLEQ_INSERT_TAIL(&chp->ch_queue->queue_xfer, xfer, |
c_xferchain); |
c_xferchain); |
else |
else |
TAILQ_INSERT_HEAD(&chp->ch_queue->queue_xfer, xfer, |
SIMPLEQ_INSERT_HEAD(&chp->ch_queue->queue_xfer, xfer, |
c_xferchain); |
c_xferchain); |
|
|
/* |
/* |
Line 1073 ata_exec_xfer(struct ata_channel *chp, s |
|
Line 1026 ata_exec_xfer(struct ata_channel *chp, s |
|
*/ |
*/ |
if ((xfer->c_flags & (C_POLL | C_WAIT)) == (C_POLL | C_WAIT)) { |
if ((xfer->c_flags & (C_POLL | C_WAIT)) == (C_POLL | C_WAIT)) { |
while (chp->ch_queue->queue_active > 0 || |
while (chp->ch_queue->queue_active > 0 || |
TAILQ_FIRST(&chp->ch_queue->queue_xfer) != xfer) { |
SIMPLEQ_FIRST(&chp->ch_queue->queue_xfer) != xfer) { |
xfer->c_flags |= C_WAITACT; |
xfer->c_flags |= C_WAITACT; |
cv_wait(&xfer->c_active, &chp->ch_lock); |
cv_wait(&chp->ch_queue->c_active, &chp->ch_lock); |
xfer->c_flags &= ~C_WAITACT; |
xfer->c_flags &= ~C_WAITACT; |
|
} |
|
|
/* |
/* |
* Free xfer now if it there was attempt to free it |
* Free xfer now if it there was attempt to free it |
* while we were waiting. |
* while we were waiting. |
*/ |
*/ |
if ((xfer->c_flags & (C_FREE|C_WAITTIMO)) == C_FREE) { |
if ((xfer->c_flags & (C_FREE|C_WAITTIMO)) == C_FREE) { |
ata_channel_unlock(chp); |
ata_channel_unlock(chp); |
|
|
ata_free_xfer(chp, xfer); |
ata_free_xfer(chp, xfer); |
return; |
return; |
} |
|
} |
} |
} |
} |
|
|
Line 1104 ata_exec_xfer(struct ata_channel *chp, s |
|
Line 1057 ata_exec_xfer(struct ata_channel *chp, s |
|
* are shared. |
* are shared. |
* |
* |
* MUST BE CALLED AT splbio()! |
* MUST BE CALLED AT splbio()! |
|
* |
|
* XXX FIS-based switching with PMP |
|
* Currently atastart() never schedules concurrent NCQ transfers to more than |
|
* one drive, even when channel has several SATA drives attached via PMP. |
|
* To support concurrent transfers to different drives with PMP, it would be |
|
* necessary to implement FIS-based switching support in controller driver, |
|
* and then adjust error handling and recovery to stop assuming at most |
|
* one active drive. |
*/ |
*/ |
void |
void |
atastart(struct ata_channel *chp) |
atastart(struct ata_channel *chp) |
Line 1111 atastart(struct ata_channel *chp) |
|
Line 1072 atastart(struct ata_channel *chp) |
|
struct atac_softc *atac = chp->ch_atac; |
struct atac_softc *atac = chp->ch_atac; |
struct ata_queue *chq = chp->ch_queue; |
struct ata_queue *chq = chp->ch_queue; |
struct ata_xfer *xfer, *axfer; |
struct ata_xfer *xfer, *axfer; |
bool recovery; |
bool skipq; |
|
|
#ifdef ATA_DEBUG |
#ifdef ATA_DEBUG |
int spl1, spl2; |
int spl1, spl2; |
Line 1129 atastart(struct ata_channel *chp) |
|
Line 1090 atastart(struct ata_channel *chp) |
|
ata_channel_lock(chp); |
ata_channel_lock(chp); |
|
|
again: |
again: |
KASSERT(chq->queue_active <= chq->queue_openings); |
/* is there a xfer ? */ |
if (chq->queue_active == chq->queue_openings) { |
if ((xfer = SIMPLEQ_FIRST(&chp->ch_queue->queue_xfer)) == NULL) { |
ATADEBUG_PRINT(("%s(chp=%p): channel %d completely busy\n", |
ATADEBUG_PRINT(("%s(chp=%p): channel %d queue_xfer is empty\n", |
__func__, chp, chp->ch_channel), DEBUG_XFERS); |
__func__, chp, chp->ch_channel), DEBUG_XFERS); |
goto out; |
goto out; |
} |
} |
|
|
/* is there a xfer ? */ |
/* |
if ((xfer = TAILQ_FIRST(&chp->ch_queue->queue_xfer)) == NULL) { |
* if someone is waiting for the command to be active, wake it up |
ATADEBUG_PRINT(("%s(chp=%p): channel %d queue_xfer is empty\n", |
* and let it process the command |
__func__, chp, chp->ch_channel), DEBUG_XFERS); |
*/ |
|
if (__predict_false(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); |
|
cv_broadcast(&chp->ch_queue->c_active); |
goto out; |
goto out; |
} |
} |
|
|
recovery = ISSET(xfer->c_flags, C_RECOVERY); |
skipq = ISSET(xfer->c_flags, C_SKIP_QUEUE); |
|
|
/* is the queue frozen? */ |
/* is the queue frozen? */ |
if (__predict_false(!recovery && chq->queue_freeze > 0)) { |
if (__predict_false(!skipq && chq->queue_freeze > 0)) { |
if (chq->queue_flags & QF_IDLE_WAIT) { |
if (chq->queue_flags & QF_IDLE_WAIT) { |
chq->queue_flags &= ~QF_IDLE_WAIT; |
chq->queue_flags &= ~QF_IDLE_WAIT; |
cv_signal(&chp->ch_queue->queue_idle); |
cv_signal(&chp->ch_queue->queue_idle); |
} |
} |
ATADEBUG_PRINT(("%s(chp=%p): channel %d drive %d " |
ATADEBUG_PRINT(("%s(chp=%p): channel %d drive %d " |
"queue frozen: %d (recovery: %d)\n", |
"queue frozen: %d\n", |
__func__, chp, chp->ch_channel, xfer->c_drive, |
__func__, chp, chp->ch_channel, xfer->c_drive, |
chq->queue_freeze, recovery), |
chq->queue_freeze), |
DEBUG_XFERS); |
DEBUG_XFERS); |
goto out; |
goto out; |
} |
} |
|
|
* Need only check first xfer. |
* Need only check first xfer. |
* XXX FIS-based switching - revisit |
* XXX FIS-based switching - revisit |
*/ |
*/ |
if (!recovery && (axfer = TAILQ_FIRST(&chp->ch_queue->active_xfers))) { |
if (!skipq && (axfer = TAILQ_FIRST(&chp->ch_queue->active_xfers))) { |
if (!ISSET(xfer->c_flags, C_NCQ) || |
if (!ISSET(xfer->c_flags, C_NCQ) || |
!ISSET(axfer->c_flags, C_NCQ) || |
!ISSET(axfer->c_flags, C_NCQ) || |
xfer->c_drive != axfer->c_drive) |
xfer->c_drive != axfer->c_drive) |
|
|
struct ata_drive_datas * const drvp = &chp->ch_drive[xfer->c_drive]; |
struct ata_drive_datas * const drvp = &chp->ch_drive[xfer->c_drive]; |
|
|
/* |
/* |
* if someone is waiting for the command to be active, wake it up |
* Are we on limit of active xfers ? If the queue has more |
* and let it process the command |
* than 1 openings, we keep one slot reserved for recovery or dump. |
*/ |
*/ |
if (xfer->c_flags & C_WAITACT) { |
KASSERT(chq->queue_active <= chq->queue_openings); |
ATADEBUG_PRINT(("atastart: xfer %p channel %d drive %d " |
const uint8_t chq_openings = (!skipq && chq->queue_openings > 1) |
"wait active\n", xfer, chp->ch_channel, xfer->c_drive), |
? (chq->queue_openings - 1) : chq->queue_openings; |
DEBUG_XFERS); |
const uint8_t drv_openings = ISSET(xfer->c_flags, C_NCQ) |
cv_signal(&xfer->c_active); |
? drvp->drv_openings : ATA_MAX_OPENINGS; |
|
if (chq->queue_active >= MIN(chq_openings, drv_openings)) { |
|
if (skipq) { |
|
panic("%s: channel %d busy, xfer not possible", |
|
__func__, chp->ch_channel); |
|
} |
|
|
|
ATADEBUG_PRINT(("%s(chp=%p): channel %d completely busy\n", |
|
__func__, chp, chp->ch_channel), DEBUG_XFERS); |
goto out; |
goto out; |
} |
} |
|
|
if (atac->atac_claim_hw) |
/* Slot allocation can fail if drv_openings < ch_openings */ |
if (!atac->atac_claim_hw(chp, 0)) |
if (!ata_queue_alloc_slot(chp, &xfer->c_slot, drv_openings)) |
|
goto out; |
|
|
|
if (__predict_false(atac->atac_claim_hw)) { |
|
if (!atac->atac_claim_hw(chp, 0)) { |
|
ata_queue_free_slot(chp, xfer->c_slot); |
goto out; |
goto out; |
|
} |
|
} |
|
|
|
/* Now committed to start the xfer */ |
|
|
ATADEBUG_PRINT(("%s(chp=%p): xfer %p channel %d drive %d\n", |
ATADEBUG_PRINT(("%s(chp=%p): xfer %p channel %d drive %d\n", |
__func__, chp, xfer, chp->ch_channel, xfer->c_drive), DEBUG_XFERS); |
__func__, chp, xfer, chp->ch_channel, xfer->c_drive), DEBUG_XFERS); |
|
|
else |
else |
CLR(chp->ch_flags, ATACH_NCQ); |
CLR(chp->ch_flags, ATACH_NCQ); |
|
|
|
SIMPLEQ_REMOVE_HEAD(&chq->queue_xfer, c_xferchain); |
|
|
ata_activate_xfer_locked(chp, xfer); |
ata_activate_xfer_locked(chp, xfer); |
|
|
if (atac->atac_cap & ATAC_CAP_NOIRQ) |
if (atac->atac_cap & ATAC_CAP_NOIRQ) |
|
|
break; |
break; |
} |
} |
|
|
/* Queue more commands if possible, but not during recovery */ |
/* Queue more commands if possible, but not during recovery or dump */ |
if (!recovery && chq->queue_active < chq->queue_openings) |
if (!skipq && chq->queue_active < chq->queue_openings) |
goto again; |
goto again; |
|
|
out: |
out: |
Line 1238 ata_xfer_start(struct ata_xfer *xfer) |
|
Line 1223 ata_xfer_start(struct ata_xfer *xfer) |
|
|
|
KASSERT(mutex_owned(&chp->ch_lock)); |
KASSERT(mutex_owned(&chp->ch_lock)); |
|
|
rv = xfer->c_start(chp, xfer); |
rv = xfer->ops->c_start(chp, xfer); |
switch (rv) { |
switch (rv) { |
case ATASTART_STARTED: |
case ATASTART_STARTED: |
/* nothing to do */ |
/* nothing to do */ |
Line 1250 ata_xfer_start(struct ata_xfer *xfer) |
|
Line 1235 ata_xfer_start(struct ata_xfer *xfer) |
|
case ATASTART_POLL: |
case ATASTART_POLL: |
/* can happen even in thread context for some ATAPI devices */ |
/* can happen even in thread context for some ATAPI devices */ |
ata_channel_unlock(chp); |
ata_channel_unlock(chp); |
KASSERT(xfer->c_poll != NULL); |
KASSERT(xfer->ops != NULL && xfer->ops->c_poll != NULL); |
xfer->c_poll(chp, xfer); |
xfer->ops->c_poll(chp, xfer); |
ata_channel_lock(chp); |
ata_channel_lock(chp); |
break; |
break; |
case ATASTART_ABORT: |
case ATASTART_ABORT: |
ata_channel_unlock(chp); |
ata_channel_unlock(chp); |
KASSERT(xfer->c_abort != NULL); |
KASSERT(xfer->ops != NULL && xfer->ops->c_abort != NULL); |
xfer->c_abort(chp, xfer); |
xfer->ops->c_abort(chp, xfer); |
ata_channel_lock(chp); |
ata_channel_lock(chp); |
break; |
break; |
} |
} |
Line 1271 ata_activate_xfer_locked(struct ata_chan |
|
Line 1256 ata_activate_xfer_locked(struct ata_chan |
|
struct ata_queue * const chq = chp->ch_queue; |
struct ata_queue * const chq = chp->ch_queue; |
|
|
KASSERT(mutex_owned(&chp->ch_lock)); |
KASSERT(mutex_owned(&chp->ch_lock)); |
|
|
KASSERT(chq->queue_active < chq->queue_openings); |
|
KASSERT((chq->active_xfers_used & __BIT(xfer->c_slot)) == 0); |
KASSERT((chq->active_xfers_used & __BIT(xfer->c_slot)) == 0); |
|
|
TAILQ_REMOVE(&chq->queue_xfer, xfer, c_xferchain); |
if ((xfer->c_flags & C_SKIP_QUEUE) == 0) |
if ((xfer->c_flags & C_RECOVERY) == 0) |
|
TAILQ_INSERT_TAIL(&chq->active_xfers, xfer, c_activechain); |
TAILQ_INSERT_TAIL(&chq->active_xfers, xfer, c_activechain); |
else { |
else { |
/* |
/* |
Line 1290 ata_activate_xfer_locked(struct ata_chan |
|
Line 1272 ata_activate_xfer_locked(struct ata_chan |
|
chq->queue_active++; |
chq->queue_active++; |
} |
} |
|
|
|
/* |
|
* Does it's own locking, does not require splbio(). |
|
* flags - whether to block waiting for free xfer |
|
*/ |
|
struct ata_xfer * |
|
ata_get_xfer(struct ata_channel *chp, bool waitok) |
|
{ |
|
return pool_get(&ata_xfer_pool, |
|
PR_ZERO | (waitok ? PR_WAITOK : PR_NOWAIT)); |
|
} |
|
|
|
/* |
|
* ata_deactivate_xfer() must be always called prior to ata_free_xfer() |
|
*/ |
|
void |
|
ata_free_xfer(struct ata_channel *chp, struct ata_xfer *xfer) |
|
{ |
|
struct ata_queue *chq = chp->ch_queue; |
|
|
|
ata_channel_lock(chp); |
|
|
|
if (__predict_false(xfer->c_flags & (C_WAITACT|C_WAITTIMO))) { |
|
/* Someone is waiting for this xfer, so we can't free now */ |
|
xfer->c_flags |= C_FREE; |
|
cv_broadcast(&chq->c_active); |
|
ata_channel_unlock(chp); |
|
return; |
|
} |
|
|
|
/* XXX move PIOBM and free_gw to deactivate? */ |
|
#if NATA_PIOBM /* XXX wdc dependent code */ |
|
if (__predict_false(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 |
|
|
|
if (__predict_false(chp->ch_atac->atac_free_hw)) |
|
chp->ch_atac->atac_free_hw(chp); |
|
|
|
ata_channel_unlock(chp); |
|
|
|
if (__predict_true(!ISSET(xfer->c_flags, C_PRIVATE_ALLOC))) |
|
pool_put(&ata_xfer_pool, xfer); |
|
} |
|
|
void |
void |
ata_deactivate_xfer(struct ata_channel *chp, struct ata_xfer *xfer) |
ata_deactivate_xfer(struct ata_channel *chp, struct ata_xfer *xfer) |
{ |
{ |
Line 1300 ata_deactivate_xfer(struct ata_channel * |
|
Line 1332 ata_deactivate_xfer(struct ata_channel * |
|
KASSERT(chq->queue_active > 0); |
KASSERT(chq->queue_active > 0); |
KASSERT((chq->active_xfers_used & __BIT(xfer->c_slot)) != 0); |
KASSERT((chq->active_xfers_used & __BIT(xfer->c_slot)) != 0); |
|
|
callout_stop(&xfer->c_timo_callout); |
/* Stop only when this is last active xfer */ |
|
if (chq->queue_active == 1) |
|
callout_stop(&chp->c_timo_callout); |
|
|
if (callout_invoking(&xfer->c_timo_callout)) |
if (callout_invoking(&chp->c_timo_callout)) |
xfer->c_flags |= C_WAITTIMO; |
xfer->c_flags |= C_WAITTIMO; |
|
|
TAILQ_REMOVE(&chq->active_xfers, xfer, c_activechain); |
TAILQ_REMOVE(&chq->active_xfers, xfer, c_activechain); |
chq->active_xfers_used &= ~__BIT(xfer->c_slot); |
chq->active_xfers_used &= ~__BIT(xfer->c_slot); |
chq->queue_active--; |
chq->queue_active--; |
|
|
|
ata_queue_free_slot(chp, xfer->c_slot); |
|
|
|
if (xfer->c_flags & C_WAIT) |
|
cv_broadcast(&chq->c_cmd_finish); |
|
|
ata_channel_unlock(chp); |
ata_channel_unlock(chp); |
} |
} |
|
|
Line 1332 ata_waitdrain_xfer_check(struct ata_chan |
|
Line 1371 ata_waitdrain_xfer_check(struct ata_chan |
|
if (chp->ch_drive[drive].drive_flags & ATA_DRIVE_WAITDRAIN) { |
if (chp->ch_drive[drive].drive_flags & ATA_DRIVE_WAITDRAIN) { |
ata_channel_unlock(chp); |
ata_channel_unlock(chp); |
|
|
(*xfer->c_kill_xfer)(chp, xfer, KILL_GONE); |
xfer->ops->c_kill_xfer(chp, xfer, KILL_GONE); |
|
|
ata_channel_lock(chp); |
ata_channel_lock(chp); |
chp->ch_drive[drive].drive_flags &= ~ATA_DRIVE_WAITDRAIN; |
chp->ch_drive[drive].drive_flags &= ~ATA_DRIVE_WAITDRAIN; |
Line 1356 ata_timo_xfer_check(struct ata_xfer *xfe |
|
Line 1395 ata_timo_xfer_check(struct ata_xfer *xfe |
|
|
|
ata_channel_lock(chp); |
ata_channel_lock(chp); |
|
|
callout_ack(&xfer->c_timo_callout); |
|
|
|
if (xfer->c_flags & C_WAITTIMO) { |
if (xfer->c_flags & C_WAITTIMO) { |
xfer->c_flags &= ~C_WAITTIMO; |
xfer->c_flags &= ~C_WAITTIMO; |
|
|
Line 1366 ata_timo_xfer_check(struct ata_xfer *xfe |
|
Line 1403 ata_timo_xfer_check(struct ata_xfer *xfe |
|
xfer->c_flags &= ~C_FREE; |
xfer->c_flags &= ~C_FREE; |
ata_channel_unlock(chp); |
ata_channel_unlock(chp); |
|
|
aprint_normal_dev(drvp->drv_softc, |
device_printf(drvp->drv_softc, |
"xfer %d freed while invoking timeout\n", |
"xfer %"PRIxPTR" freed while invoking timeout\n", |
xfer->c_slot); |
(intptr_t)xfer & PAGE_MASK); |
|
|
ata_free_xfer(chp, xfer); |
ata_free_xfer(chp, xfer); |
return true; |
return true; |
Line 1377 ata_timo_xfer_check(struct ata_xfer *xfe |
|
Line 1414 ata_timo_xfer_check(struct ata_xfer *xfe |
|
/* Race vs. callout_stop() in ata_deactivate_xfer() */ |
/* Race vs. callout_stop() in ata_deactivate_xfer() */ |
ata_channel_unlock(chp); |
ata_channel_unlock(chp); |
|
|
aprint_normal_dev(drvp->drv_softc, |
device_printf(drvp->drv_softc, |
"xfer %d deactivated while invoking timeout\n", |
"xfer %"PRIxPTR" deactivated while invoking timeout\n", |
xfer->c_slot); |
(intptr_t)xfer & PAGE_MASK); |
return true; |
return true; |
} |
} |
|
|
Line 1389 ata_timo_xfer_check(struct ata_xfer *xfe |
|
Line 1426 ata_timo_xfer_check(struct ata_xfer *xfe |
|
return false; |
return false; |
} |
} |
|
|
void |
|
ata_timeout(void *v) |
|
{ |
|
struct ata_xfer *xfer = v; |
|
int s; |
|
|
|
ATADEBUG_PRINT(("%s: slot %d\n", __func__, xfer->c_slot), |
|
DEBUG_FUNCS|DEBUG_XFERS); |
|
|
|
s = splbio(); /* XXX MPSAFE */ |
|
|
|
if (ata_timo_xfer_check(xfer)) { |
|
/* Already logged */ |
|
goto out; |
|
} |
|
|
|
/* Mark as timed out. Do not print anything, wd(4) will. */ |
|
xfer->c_flags |= C_TIMEOU; |
|
xfer->c_intr(xfer->c_chp, xfer, 0); |
|
|
|
out: |
|
splx(s); |
|
} |
|
|
|
/* |
/* |
* Kill off all active xfers for a ata_channel. |
* Kill off all active xfers for a ata_channel. |
* |
* |
Line 1427 ata_kill_active(struct ata_channel *chp, |
|
Line 1440 ata_kill_active(struct ata_channel *chp, |
|
KASSERT(mutex_owned(&chp->ch_lock)); |
KASSERT(mutex_owned(&chp->ch_lock)); |
|
|
TAILQ_FOREACH_SAFE(xfer, &chq->active_xfers, c_activechain, xfernext) { |
TAILQ_FOREACH_SAFE(xfer, &chq->active_xfers, c_activechain, xfernext) { |
(*xfer->c_kill_xfer)(xfer->c_chp, xfer, reason); |
ata_channel_unlock(chp); |
|
xfer->ops->c_kill_xfer(xfer->c_chp, xfer, reason); |
|
ata_channel_lock(chp); |
} |
} |
|
|
if (flags & AT_RST_EMERG) |
|
ata_queue_reset(chq); |
|
} |
} |
|
|
/* |
/* |
Line 1442 ata_kill_pending(struct ata_drive_datas |
|
Line 1454 ata_kill_pending(struct ata_drive_datas |
|
{ |
{ |
struct ata_channel * const chp = drvp->chnl_softc; |
struct ata_channel * const chp = drvp->chnl_softc; |
struct ata_queue * const chq = chp->ch_queue; |
struct ata_queue * const chq = chp->ch_queue; |
struct ata_xfer *xfer, *xfernext; |
struct ata_xfer *xfer; |
|
|
ata_channel_lock(chp); |
ata_channel_lock(chp); |
|
|
/* Kill all pending transfers */ |
/* Kill all pending transfers */ |
TAILQ_FOREACH_SAFE(xfer, &chq->queue_xfer, c_xferchain, xfernext) { |
while ((xfer = SIMPLEQ_FIRST(&chq->queue_xfer))) { |
KASSERT(xfer->c_chp == chp); |
KASSERT(xfer->c_chp == chp); |
|
|
if (xfer->c_drive != drvp->drive) |
if (xfer->c_drive != drvp->drive) |
continue; |
continue; |
|
|
TAILQ_REMOVE(&chp->ch_queue->queue_xfer, xfer, c_xferchain); |
SIMPLEQ_REMOVE_HEAD(&chp->ch_queue->queue_xfer, c_xferchain); |
|
|
/* |
/* |
* Keep the lock, so that we get deadlock (and 'locking against |
* Keep the lock, so that we get deadlock (and 'locking against |
Line 1461 ata_kill_pending(struct ata_drive_datas |
|
Line 1473 ata_kill_pending(struct ata_drive_datas |
|
* data corruption, if the hook tries to call back into |
* data corruption, if the hook tries to call back into |
* middle layer for inactive xfer. |
* middle layer for inactive xfer. |
*/ |
*/ |
(*xfer->c_kill_xfer)(chp, xfer, KILL_GONE_INACTIVE); |
xfer->ops->c_kill_xfer(chp, xfer, KILL_GONE_INACTIVE); |
} |
} |
|
|
/* Wait until all active transfers on the drive finish */ |
/* Wait until all active transfers on the drive finish */ |
Line 1506 ata_channel_freeze(struct ata_channel *c |
|
Line 1518 ata_channel_freeze(struct ata_channel *c |
|
ata_channel_unlock(chp); |
ata_channel_unlock(chp); |
} |
} |
|
|
static void |
void |
ata_channel_thaw_locked(struct ata_channel *chp) |
ata_channel_thaw_locked(struct ata_channel *chp) |
{ |
{ |
KASSERT(mutex_owned(&chp->ch_lock)); |
KASSERT(mutex_owned(&chp->ch_lock)); |
Line 1518 ata_channel_thaw_locked(struct ata_chann |
|
Line 1530 ata_channel_thaw_locked(struct ata_chann |
|
chp->ch_queue->queue_freeze), DEBUG_FUNCS | DEBUG_XFERS); |
chp->ch_queue->queue_freeze), DEBUG_FUNCS | DEBUG_XFERS); |
} |
} |
|
|
void |
|
ata_channel_thaw(struct ata_channel *chp) |
|
{ |
|
ata_channel_lock(chp); |
|
ata_channel_thaw_locked(chp); |
|
ata_channel_unlock(chp); |
|
} |
|
|
|
/* |
/* |
* ata_reset_channel: |
* ata_thread_run: |
* |
* |
* Reset and ATA channel. |
* Reset and ATA channel. Channel lock must be held. arg is type-specific. |
* |
|
* MUST BE CALLED AT splbio()! |
|
*/ |
*/ |
void |
void |
ata_reset_channel(struct ata_channel *chp, int flags) |
ata_thread_run(struct ata_channel *chp, int flags, int type, int arg) |
{ |
{ |
struct atac_softc *atac = chp->ch_atac; |
struct atac_softc *atac = chp->ch_atac; |
int drive; |
|
bool threset = false; |
bool threset = false; |
|
struct ata_drive_datas *drvp; |
|
|
#ifdef ATA_DEBUG |
ata_channel_lock_owned(chp); |
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 */ |
|
|
|
ata_channel_lock(chp); |
|
|
|
/* |
/* |
* If we can poll or wait it's OK, otherwise wake up the |
* If we can poll or wait it's OK, otherwise wake up the |
* kernel thread to do it for us. |
* kernel thread to do it for us. |
*/ |
*/ |
ATADEBUG_PRINT(("ata_reset_channel flags 0x%x ch_flags 0x%x\n", |
ATADEBUG_PRINT(("%s flags 0x%x ch_flags 0x%x\n", |
flags, chp->ch_flags), DEBUG_FUNCS | DEBUG_XFERS); |
__func__, flags, chp->ch_flags), DEBUG_FUNCS | DEBUG_XFERS); |
if ((flags & (AT_POLL | AT_WAIT)) == 0) { |
if ((flags & (AT_POLL | AT_WAIT)) == 0) { |
if (chp->ch_flags & ATACH_TH_RESET) { |
switch (type) { |
/* No need to schedule a reset more than one time. */ |
case ATACH_TH_RESET: |
ata_channel_unlock(chp); |
if (chp->ch_flags & ATACH_TH_RESET) { |
return; |
/* No need to schedule another reset */ |
|
return; |
|
} |
|
break; |
|
case ATACH_TH_DRIVE_RESET: |
|
{ |
|
int drive = arg; |
|
|
|
KASSERT(drive <= chp->ch_ndrives); |
|
drvp = &chp->ch_drive[drive]; |
|
|
|
if (drvp->drive_flags & ATA_DRIVE_TH_RESET) { |
|
/* No need to schedule another reset */ |
|
return; |
|
} |
|
drvp->drive_flags |= ATA_DRIVE_TH_RESET; |
|
break; |
|
} |
|
case ATACH_TH_RECOVERY: |
|
{ |
|
uint32_t tfd = (uint32_t)arg; |
|
|
|
KASSERT((chp->ch_flags & ATACH_RECOVERING) == 0); |
|
chp->recovery_tfd = tfd; |
|
break; |
|
} |
|
default: |
|
panic("%s: unknown type: %x", __func__, type); |
|
/* NOTREACHED */ |
} |
} |
|
|
/* |
/* |
Line 1573 ata_reset_channel(struct ata_channel *ch |
|
Line 1590 ata_reset_channel(struct ata_channel *ch |
|
* to a thread. |
* to a thread. |
*/ |
*/ |
ata_channel_freeze_locked(chp); |
ata_channel_freeze_locked(chp); |
chp->ch_flags |= ATACH_TH_RESET; |
chp->ch_flags |= type; |
chp->ch_reset_flags = flags & AT_RST_EMERG; |
|
cv_signal(&chp->ch_thr_idle); |
cv_signal(&chp->ch_thr_idle); |
ata_channel_unlock(chp); |
|
return; |
return; |
} |
} |
|
|
Line 1588 ata_reset_channel(struct ata_channel *ch |
|
Line 1604 ata_reset_channel(struct ata_channel *ch |
|
* the flag now so that the thread won't try to execute it if |
* the flag now so that the thread won't try to execute it if |
* we happen to sleep, and thaw one more time after the reset. |
* we happen to sleep, and thaw one more time after the reset. |
*/ |
*/ |
if (chp->ch_flags & ATACH_TH_RESET) { |
if (chp->ch_flags & type) { |
chp->ch_flags &= ~ATACH_TH_RESET; |
chp->ch_flags &= ~type; |
threset = true; |
threset = true; |
} |
} |
|
|
ata_channel_unlock(chp); |
switch (type) { |
|
case ATACH_TH_RESET: |
|
(*atac->atac_bustype_ata->ata_reset_channel)(chp, flags); |
|
|
(*atac->atac_bustype_ata->ata_reset_channel)(chp, flags); |
KASSERT(chp->ch_ndrives == 0 || chp->ch_drive != NULL); |
|
for (int drive = 0; drive < chp->ch_ndrives; drive++) |
|
chp->ch_drive[drive].state = 0; |
|
break; |
|
|
ata_channel_lock(chp); |
case ATACH_TH_DRIVE_RESET: |
KASSERT(chp->ch_ndrives == 0 || chp->ch_drive != NULL); |
{ |
for (drive = 0; drive < chp->ch_ndrives; drive++) |
int drive = arg; |
chp->ch_drive[drive].state = 0; |
|
|
KASSERT(drive <= chp->ch_ndrives); |
|
drvp = &chp->ch_drive[drive]; |
|
(*atac->atac_bustype_ata->ata_reset_drive)(drvp, flags, NULL); |
|
drvp->state = 0; |
|
break; |
|
} |
|
|
|
case ATACH_TH_RECOVERY: |
|
{ |
|
uint32_t tfd = (uint32_t)arg; |
|
|
|
KASSERT((chp->ch_flags & ATACH_RECOVERING) == 0); |
|
KASSERT(atac->atac_bustype_ata->ata_recovery != NULL); |
|
|
|
SET(chp->ch_flags, ATACH_RECOVERING); |
|
(*atac->atac_bustype_ata->ata_recovery)(chp, flags, tfd); |
|
CLR(chp->ch_flags, ATACH_RECOVERING); |
|
break; |
|
} |
|
|
|
default: |
|
panic("%s: unknown type: %x", __func__, type); |
|
/* NOTREACHED */ |
|
} |
|
|
/* |
/* |
* Thaw one extra time to clear the freeze done when the reset has |
* Thaw one extra time to clear the freeze done when the reset has |
Line 1614 ata_reset_channel(struct ata_channel *ch |
|
Line 1659 ata_reset_channel(struct ata_channel *ch |
|
|
|
/* Signal the thread in case there is an xfer to run */ |
/* Signal the thread in case there is an xfer to run */ |
cv_signal(&chp->ch_thr_idle); |
cv_signal(&chp->ch_thr_idle); |
|
|
ata_channel_unlock(chp); |
|
|
|
if (flags & AT_RST_EMERG) { |
|
/* make sure that we can use polled commands */ |
|
ata_queue_reset(chp->ch_queue); |
|
} else { |
|
atastart(chp); |
|
} |
|
} |
} |
|
|
int |
int |
Line 1731 ata_downgrade_mode(struct ata_drive_data |
|
Line 1767 ata_downgrade_mode(struct ata_drive_data |
|
device_t drv_dev = drvp->drv_softc; |
device_t drv_dev = drvp->drv_softc; |
int cf_flags = device_cfdata(drv_dev)->cf_flags; |
int cf_flags = device_cfdata(drv_dev)->cf_flags; |
|
|
|
ata_channel_lock_owned(drvp->chnl_softc); |
|
|
/* if drive or controller don't know its mode, we can't do much */ |
/* if drive or controller don't know its mode, we can't do much */ |
if ((drvp->drive_flags & ATA_DRIVE_MODE) == 0 || |
if ((drvp->drive_flags & ATA_DRIVE_MODE) == 0 || |
(atac->atac_set_modes == NULL)) |
(atac->atac_set_modes == NULL)) |
Line 1768 ata_downgrade_mode(struct ata_drive_data |
|
Line 1806 ata_downgrade_mode(struct ata_drive_data |
|
(*atac->atac_set_modes)(chp); |
(*atac->atac_set_modes)(chp); |
ata_print_modes(chp); |
ata_print_modes(chp); |
/* reset the channel, which will schedule all drives for setup */ |
/* reset the channel, which will schedule all drives for setup */ |
ata_reset_channel(chp, flags); |
ata_thread_run(chp, flags, ATACH_TH_RESET, ATACH_NODRIVE); |
return 1; |
return 1; |
} |
} |
#endif /* NATA_DMA */ |
#endif /* NATA_DMA */ |
Line 1855 ata_probe_caps(struct ata_drive_datas *d |
|
Line 1893 ata_probe_caps(struct ata_drive_datas *d |
|
*/ |
*/ |
if (atac->atac_set_modes) |
if (atac->atac_set_modes) |
/* |
/* |
* It's OK to pool here, it's fast enough |
* It's OK to poll here, it's fast enough |
* to not bother waiting for interrupt |
* to not bother waiting for interrupt |
*/ |
*/ |
if (ata_set_mode(drvp, 0x08 | (i + 3), |
if (ata_set_mode(drvp, 0x08 | (i + 3), |
Line 1994 ata_probe_caps(struct ata_drive_datas *d |
|
Line 2032 ata_probe_caps(struct ata_drive_datas *d |
|
#if NATA_DMA |
#if NATA_DMA |
if ((atac->atac_cap & ATAC_CAP_DMA) == 0) { |
if ((atac->atac_cap & ATAC_CAP_DMA) == 0) { |
/* don't care about DMA modes */ |
/* don't care about DMA modes */ |
|
if (*sep != '\0') |
|
aprint_verbose("\n"); |
return; |
return; |
} |
} |
if (cf_flags & ATA_CONFIG_DMA_SET) { |
if (cf_flags & ATA_CONFIG_DMA_SET) { |
Line 2040 ata_probe_caps(struct ata_drive_datas *d |
|
Line 2080 ata_probe_caps(struct ata_drive_datas *d |
|
} |
} |
ata_channel_unlock(chp); |
ata_channel_unlock(chp); |
|
|
if (printed) |
if (*sep != '\0') |
aprint_verbose("\n"); |
aprint_verbose("\n"); |
|
|
#if NATA_UDMA |
#if NATA_UDMA |
Line 2108 atabusioctl(dev_t dev, u_long cmd, void |
|
Line 2148 atabusioctl(dev_t dev, u_long cmd, void |
|
struct ata_channel *chp = sc->sc_chan; |
struct ata_channel *chp = sc->sc_chan; |
int min_drive, max_drive, drive; |
int min_drive, max_drive, drive; |
int error; |
int error; |
int s; |
|
|
|
/* |
/* |
* Enforce write permission for ioctls that change the |
* Enforce write permission for ioctls that change the |
Line 2125 atabusioctl(dev_t dev, u_long cmd, void |
|
Line 2164 atabusioctl(dev_t dev, u_long cmd, void |
|
|
|
switch (cmd) { |
switch (cmd) { |
case ATABUSIORESET: |
case ATABUSIORESET: |
s = splbio(); |
ata_channel_lock(chp); |
ata_reset_channel(sc->sc_chan, AT_WAIT | AT_POLL); |
ata_thread_run(sc->sc_chan, AT_WAIT | AT_POLL, |
splx(s); |
ATACH_TH_RESET, ATACH_NODRIVE); |
|
ata_channel_unlock(chp); |
return 0; |
return 0; |
case ATABUSIOSCAN: |
case ATABUSIOSCAN: |
{ |
{ |
Line 2205 atabus_resume(device_t dv, const pmf_qua |
|
Line 2245 atabus_resume(device_t dv, const pmf_qua |
|
/* unfreeze the queue and reset drives */ |
/* unfreeze the queue and reset drives */ |
ata_channel_thaw_locked(chp); |
ata_channel_thaw_locked(chp); |
|
|
ata_channel_unlock(chp); |
|
|
|
/* reset channel only if there are drives attached */ |
/* reset channel only if there are drives attached */ |
if (chp->ch_ndrives > 0) |
if (chp->ch_ndrives > 0) |
ata_reset_channel(chp, AT_WAIT); |
ata_thread_run(chp, AT_WAIT, ATACH_TH_RESET, ATACH_NODRIVE); |
|
|
|
ata_channel_unlock(chp); |
|
|
out: |
out: |
return true; |
return true; |
Line 2240 atabus_rescan(device_t self, const char |
|
Line 2280 atabus_rescan(device_t self, const char |
|
} |
} |
} |
} |
|
|
initq = malloc(sizeof(*initq), M_DEVBUF, M_WAITOK); |
initq = kmem_zalloc(sizeof(*initq), KM_SLEEP); |
initq->atabus_sc = sc; |
initq->atabus_sc = sc; |
mutex_enter(&atabus_qlock); |
mutex_enter(&atabus_qlock); |
TAILQ_INSERT_TAIL(&atabus_initq_head, initq, atabus_initq); |
TAILQ_INSERT_TAIL(&atabus_initq_head, initq, atabus_initq); |
Line 2258 atabus_rescan(device_t self, const char |
|
Line 2298 atabus_rescan(device_t self, const char |
|
void |
void |
ata_delay(struct ata_channel *chp, int ms, const char *msg, int flags) |
ata_delay(struct ata_channel *chp, int ms, const char *msg, int flags) |
{ |
{ |
|
KASSERT(mutex_owned(&chp->ch_lock)); |
|
|
if ((flags & (AT_WAIT | AT_POLL)) == AT_POLL) { |
if ((flags & (AT_WAIT | AT_POLL)) == AT_POLL) { |
/* |
/* |
Line 2268 ata_delay(struct ata_channel *chp, int m |
|
Line 2309 ata_delay(struct ata_channel *chp, int m |
|
} else { |
} else { |
int pause = mstohz(ms); |
int pause = mstohz(ms); |
|
|
KASSERT(mutex_owned(&chp->ch_lock)); |
|
kpause(msg, false, pause > 0 ? pause : 1, &chp->ch_lock); |
kpause(msg, false, pause > 0 ? pause : 1, &chp->ch_lock); |
} |
} |
} |
} |
Line 2304 atacmd_toncq(struct ata_xfer *xfer, uint |
|
Line 2344 atacmd_toncq(struct ata_xfer *xfer, uint |
|
} |
} |
|
|
void |
void |
ata_wait_xfer(struct ata_channel *chp, struct ata_xfer *xfer) |
ata_wait_cmd(struct ata_channel *chp, struct ata_xfer *xfer) |
{ |
{ |
KASSERT(mutex_owned(&chp->ch_lock)); |
struct ata_queue *chq = chp->ch_queue; |
|
struct ata_command *ata_c = &xfer->c_ata_c; |
|
|
cv_wait(&xfer->c_finish, &chp->ch_lock); |
ata_channel_lock(chp); |
} |
|
|
|
void |
while ((ata_c->flags & AT_DONE) == 0) |
ata_wake_xfer(struct ata_channel *chp, struct ata_xfer *xfer) |
cv_wait(&chq->c_cmd_finish, &chp->ch_lock); |
{ |
|
KASSERT(mutex_owned(&chp->ch_lock)); |
ata_channel_unlock(chp); |
|
|
cv_signal(&xfer->c_finish); |
KASSERT((ata_c->flags & AT_DONE) != 0); |
} |
} |