Fix IRQ allocation bug on controllers using a shared interrupt.

Fix a bug which could cause panics in ad/atapi-interrupt.

Add support for UDMA66 on Promise Ultra/Fasttrak controllers.

Get rid of ATA_IGNORE_INTR, and introduce ATA_WAIT_INTR instead.

Add a delay in the dump routine in ata-disk.c, some controllers
seem to need this. Also dont use the timeout watchdog when dumping.

Disable DMA on ATAPI devices as default, add option ATA_ENABLE_ATAPI_DMA
for those that has HW that works.

Add support for some not-up-to-spec ATAPI devices that returns data
together with completition status on data moving cmd's.
This commit is contained in:
Søren Schmidt 1999-11-06 16:50:21 +00:00
parent 8db34b3a11
commit 3de25e2f32
7 changed files with 149 additions and 78 deletions

View File

@ -200,6 +200,7 @@ ISP_DISABLE_2200_SUPPORT opt_isp.h
# Options used in the 'ata' ATA/ATAPI driver
ATA_STATIC_ID opt_ata.h
ATA_16BIT_ONLY opt_ata.h
ATA_ENABLE_ATAPI_DMA opt_ata.h
# Resource limits.
DFLDSIZ opt_rlimit.h

View File

@ -243,6 +243,7 @@ ata_pciattach(device_t dev)
u_int32_t cmd;
int32_t iobase_1, iobase_2, altiobase_1, altiobase_2;
int32_t bmaddr_1 = 0, bmaddr_2 = 0, irq1, irq2;
struct resource *irq = NULL;
int32_t lun;
/* set up vendor-specific stuff */
@ -324,7 +325,6 @@ ata_pciattach(device_t dev)
alpha_platform_setup_ide_intr(0, ataintr, scp);
#endif
else {
struct resource *irq;
int rid = 0;
void *ih;
@ -348,13 +348,14 @@ ata_pciattach(device_t dev)
alpha_platform_setup_ide_intr(1, ataintr, scp);
#endif
else {
struct resource *irq;
int rid = 0;
void *ih;
if (!(irq = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0, 1,
RF_SHAREABLE | RF_ACTIVE)))
printf("ata_pciattach: Unable to alloc interrupt\n");
if (irq1 != irq2 || irq == NULL) {
if (!(irq = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0, 1,
RF_SHAREABLE | RF_ACTIVE)))
printf("ata_pciattach: Unable to alloc interrupt\n");
}
bus_setup_intr(dev, irq, INTR_TYPE_BIO, ataintr, scp, &ih);
}
printf("ata%d at 0x%04x irq %d on ata-pci%d\n",
@ -514,33 +515,37 @@ ataintr(void *data)
if (scp->flags & ATA_DMA_ACTIVE)
if (!(ata_dmastatus(scp) & ATA_BMSTAT_INTERRUPT))
return;
if ((scp->status = inb(scp->ioaddr + ATA_STATUS)) == ATA_S_BUSY) /*XXX SOS*/
if ((scp->status = inb(scp->ioaddr + ATA_STATUS)) == ATA_S_BUSY)
return;
/* find & call the responsible driver to process this interrupt */
switch (scp->active) {
#if NATADISK > 0
case ATA_ACTIVE_ATA:
if (scp->running && (ad_interrupt(scp->running) == ATA_OP_CONTINUES))
return;
if (!scp->running)
return;
if (ad_interrupt(scp->running) == ATA_OP_CONTINUES)
return;
break;
#endif
#if NATAPICD > 0 || NATAPIFD > 0 || NATAPIST > 0
case ATA_ACTIVE_ATAPI:
if (scp->running && (atapi_interrupt(scp->running) == ATA_OP_CONTINUES))
return;
if (!scp->running)
return;
if (atapi_interrupt(scp->running) == ATA_OP_CONTINUES)
return;
break;
#endif
case ATA_WAIT_INTR:
wakeup((caddr_t)scp);
break;
case ATA_WAIT_READY:
break;
case ATA_REINITING:
return;
case ATA_IGNORE_INTR:
break;
default:
case ATA_IDLE:
#ifdef ATA_DEBUG
@ -780,7 +785,7 @@ ata_command(struct ata_softc *scp, int32_t device, u_int32_t command,
switch (flags) {
case ATA_WAIT_INTR:
if (scp->active != ATA_IDLE)
printf("DANGER wait_intr active=%s\n", active2str(scp->active));
printf("WARNING: WAIT_INTR active=%s\n", active2str(scp->active));
scp->active = ATA_WAIT_INTR;
outb(scp->ioaddr + ATA_CMD, command);
if (tsleep((caddr_t)scp, PRIBIO, "atacmd", 500)) {
@ -790,12 +795,17 @@ ata_command(struct ata_softc *scp, int32_t device, u_int32_t command,
}
break;
case ATA_IGNORE_INTR:
case ATA_WAIT_READY:
if (scp->active != ATA_IDLE && scp->active != ATA_REINITING)
printf("DANGER ignore_intr active=%s\n", active2str(scp->active));
if (scp->active != ATA_REINITING)
scp->active = ATA_IGNORE_INTR;
printf("WARNING: WAIT_READY active=%s\n", active2str(scp->active));
scp->active = ATA_WAIT_READY;
outb(scp->ioaddr + ATA_CMD, command);
if (ata_wait(scp, device, ATA_S_READY) < 0) {
printf("ata%d-%s: timeout waiting for command=%02x s=%02x e=%02x\n",
scp->lun, device ? "slave" : "master", command,
scp->status, scp->error);
return -1;
}
break;
case ATA_IMMEDIATE:
@ -803,7 +813,7 @@ ata_command(struct ata_softc *scp, int32_t device, u_int32_t command,
break;
default:
printf("DANGER illegal interrupt flag=%s\n", active2str(flags));
printf("DANGER: illegal interrupt flag=%s\n", active2str(flags));
}
#ifdef ATA_DEBUG
printf("ata_command: leaving\n");
@ -838,8 +848,6 @@ active2str(int32_t active)
return("ATA_IDLE");
case ATA_WAIT_INTR:
return("ATA_WAIT_INTR");
case ATA_IGNORE_INTR:
return("ATA_IGNORE_INTR");
case ATA_ACTIVE_ATA:
return("ATA_ACTIVE_ATA");
case ATA_ACTIVE_ATAPI:

View File

@ -155,7 +155,7 @@ struct ata_softc {
#define ATA_IDLE 0x0
#define ATA_IMMEDIATE 0x0
#define ATA_WAIT_INTR 0x1
#define ATA_IGNORE_INTR 0x2
#define ATA_WAIT_READY 0x2
#define ATA_ACTIVE_ATA 0x3
#define ATA_ACTIVE_ATAPI 0x4
#define ATA_REINITING 0x5

View File

@ -322,6 +322,7 @@ addump(dev_t dev)
adp->flags &= ~AD_F_DMA_ENABLED;
while (count > 0) {
DELAY(1000);
if (is_physical_memory(addr))
pmap_enter(kernel_pmap, (vm_offset_t)CADDR1,
trunc_page(addr), VM_PROT_READ, TRUE);
@ -416,7 +417,11 @@ ad_transfer(struct ad_request *request)
if (request->donecount == 0) {
/* start timeout for this transfer */
request->timeout_handle = timeout((timeout_t*)ad_timeout, request, 3*hz);
if (panicstr)
request->timeout_handle.callout = NULL;
else
request->timeout_handle =
timeout((timeout_t*)ad_timeout, request, 3*hz);
/* setup transfer parameters */
count = howmany(request->bytecount, DEV_BSIZE);
@ -614,6 +619,7 @@ ad_timeout(struct ad_request *request)
{
struct ad_softc *adp = request->device;
adp->controller->running = NULL;
printf("ata%d-%s: ad_timeout: lost disk contact - resetting\n",
adp->controller->lun,
(adp->unit == ATA_MASTER) ? "master" : "slave");

View File

@ -100,7 +100,7 @@ ata_dmainit(struct ata_softc *scp, int32_t device,
int32_t mask48, new48;
error = ata_command(scp, device, ATA_C_SETFEATURES, 0, 0, 0,
ATA_UDMA2, ATA_C_FEA_SETXFER, ATA_IGNORE_INTR);
ATA_UDMA2, ATA_C_FEA_SETXFER, ATA_WAIT_READY);
if (bootverbose)
printf("ata%d: %s: %s setting up UDMA2 mode on PIIX4 chip\n",
scp->lun, (device == ATA_MASTER) ? "master" : "slave",
@ -139,7 +139,7 @@ ata_dmainit(struct ata_softc *scp, int32_t device,
pci_write_config(scp->dev, 0x44, new44, 4);
}
error = ata_command(scp, device, ATA_C_SETFEATURES, 0, 0, 0,
ATA_WDMA2, ATA_C_FEA_SETXFER, ATA_IGNORE_INTR);
ATA_WDMA2, ATA_C_FEA_SETXFER, ATA_WAIT_READY);
if (bootverbose)
printf("ata%d: %s: %s setting up WDMA2 mode on PIIX4 chip\n",
scp->lun, (device == ATA_MASTER) ? "master" : "slave",
@ -212,7 +212,7 @@ ata_dmainit(struct ata_softc *scp, int32_t device,
int32_t word54 = pci_read_config(scp->dev, 0x54, 4);
error = ata_command(scp, device, ATA_C_SETFEATURES, 0, 0, 0,
ATA_UDMA2, ATA_C_FEA_SETXFER, ATA_IGNORE_INTR);
ATA_UDMA2, ATA_C_FEA_SETXFER, ATA_WAIT_READY);
if (bootverbose)
printf("ata%d: %s: %s setting up UDMA2 mode on Aladdin chip\n",
scp->lun, (device == ATA_MASTER) ? "master" : "slave",
@ -228,7 +228,7 @@ ata_dmainit(struct ata_softc *scp, int32_t device,
}
else if (wdmamode >= 2 && apiomode >= 4) {
error = ata_command(scp, device, ATA_C_SETFEATURES, 0, 0, 0,
ATA_WDMA2, ATA_C_FEA_SETXFER, ATA_IGNORE_INTR);
ATA_WDMA2, ATA_C_FEA_SETXFER, ATA_WAIT_READY);
if (bootverbose)
printf("ata%d: %s: %s setting up WDMA2 mode on Aladdin chip\n",
scp->lun, (device == ATA_MASTER) ? "master" : "slave",
@ -248,9 +248,24 @@ ata_dmainit(struct ata_softc *scp, int32_t device,
break;
devno = (scp->unit << 1) + ((device == ATA_MASTER) ? 0 : 1);
if (udmamode >=4 && type == 0x4d38105a &&
!(pci_read_config(scp->dev, 0x50, 2)&(scp->unit ? 1<<11 : 1<<10))) {
error = ata_command(scp, device, ATA_C_SETFEATURES, 0, 0, 0,
ATA_UDMA4, ATA_C_FEA_SETXFER, ATA_WAIT_READY);
if (bootverbose)
printf("ata%d: %s: %s setting up UDMA4 mode on Promise chip\n",
scp->lun, (device == ATA_MASTER) ? "master" : "slave",
(error) ? "failed" : "success");
if (error)
break;
outb(scp->bmaddr + 0x11, inl(scp->bmaddr + 0x11) | scp->unit ? 8:2);
pci_write_config(scp->dev, 0x60 + (devno << 2), 0x004127f3, 4);
scp->mode[(device == ATA_MASTER) ? 0 : 1] = ATA_MODE_UDMA4;
return 0;
}
if (udmamode >=2) {
error = ata_command(scp, device, ATA_C_SETFEATURES, 0, 0, 0,
ATA_UDMA2, ATA_C_FEA_SETXFER, ATA_IGNORE_INTR);
ATA_UDMA2, ATA_C_FEA_SETXFER, ATA_WAIT_READY);
if (bootverbose)
printf("ata%d: %s: %s setting up UDMA2 mode on Promise chip\n",
scp->lun, (device == ATA_MASTER) ? "master" : "slave",
@ -263,7 +278,7 @@ ata_dmainit(struct ata_softc *scp, int32_t device,
}
else if (wdmamode >= 2 && apiomode >= 4) {
error = ata_command(scp, device, ATA_C_SETFEATURES, 0, 0, 0,
ATA_WDMA2, ATA_C_FEA_SETXFER, ATA_IGNORE_INTR);
ATA_WDMA2, ATA_C_FEA_SETXFER, ATA_WAIT_READY);
if (bootverbose)
printf("ata%d: %s: %s setting up WDMA2 mode on Promise chip\n",
scp->lun, (device == ATA_MASTER) ? "master" : "slave",
@ -291,7 +306,7 @@ ata_dmainit(struct ata_softc *scp, int32_t device,
devno = (device == ATA_MASTER) ? 0 : 1;
if (udmamode >=4 && !(pci_read_config(scp->dev, 0x5a, 1) & 0x2)) {
error = ata_command(scp, device, ATA_C_SETFEATURES, 0, 0, 0,
ATA_UDMA4, ATA_C_FEA_SETXFER, ATA_IGNORE_INTR);
ATA_UDMA4, ATA_C_FEA_SETXFER, ATA_WAIT_READY);
if (bootverbose)
printf("ata%d: %s: %s setting up UDMA4 mode on HPT366 chip\n",
scp->lun, (device == ATA_MASTER) ? "master" : "slave",
@ -302,9 +317,9 @@ ata_dmainit(struct ata_softc *scp, int32_t device,
scp->mode[(device == ATA_MASTER) ? 0 : 1] = ATA_MODE_UDMA4;
return 0;
}
else if (udmamode >=3) {
if (udmamode >=3 && !(pci_read_config(scp->dev, 0x5a, 1) & 0x2)) {
error = ata_command(scp, device, ATA_C_SETFEATURES, 0, 0, 0,
ATA_UDMA3, ATA_C_FEA_SETXFER, ATA_IGNORE_INTR);
ATA_UDMA3, ATA_C_FEA_SETXFER, ATA_WAIT_READY);
if (bootverbose)
printf("ata%d: %s: %s setting up UDMA3 mode on HPT366 chip\n",
scp->lun, (device == ATA_MASTER) ? "master" : "slave",
@ -315,9 +330,9 @@ ata_dmainit(struct ata_softc *scp, int32_t device,
scp->mode[(device == ATA_MASTER) ? 0 : 1] = ATA_MODE_UDMA3;
return 0;
}
else if (udmamode >=2) {
if (udmamode >=2) {
error = ata_command(scp, device, ATA_C_SETFEATURES, 0, 0, 0,
ATA_UDMA2, ATA_C_FEA_SETXFER, ATA_IGNORE_INTR);
ATA_UDMA2, ATA_C_FEA_SETXFER, ATA_WAIT_READY);
if (bootverbose)
printf("ata%d: %s: %s setting up UDMA2 mode on HPT366 chip\n",
scp->lun, (device == ATA_MASTER) ? "master" : "slave",
@ -330,7 +345,7 @@ ata_dmainit(struct ata_softc *scp, int32_t device,
}
else if (wdmamode >= 2 && apiomode >= 4) {
error = ata_command(scp, device, ATA_C_SETFEATURES, 0, 0, 0,
ATA_WDMA2, ATA_C_FEA_SETXFER, ATA_IGNORE_INTR);
ATA_WDMA2, ATA_C_FEA_SETXFER, ATA_WAIT_READY);
if (bootverbose)
printf("ata%d: %s: %s setting up WDMA2 mode on HPT366 chip\n",
scp->lun, (device == ATA_MASTER) ? "master" : "slave",
@ -361,7 +376,7 @@ ata_dmainit(struct ata_softc *scp, int32_t device,
((device == ATA_MASTER) ?
ATA_BMSTAT_DMA_SLAVE : ATA_BMSTAT_DMA_MASTER))) {
error = ata_command(scp, device, ATA_C_SETFEATURES, 0, 0, 0,
ATA_WDMA2, ATA_C_FEA_SETXFER, ATA_IGNORE_INTR);
ATA_WDMA2, ATA_C_FEA_SETXFER, ATA_WAIT_READY);
if (bootverbose)
printf("ata%d: %s: %s setting up WDMA2 mode on generic chip\n",
scp->lun, (device == ATA_MASTER) ? "master" : "slave",

View File

@ -49,6 +49,8 @@
/* prototypes */
static void atapi_attach(void *);
static int32_t atapi_getparam(struct atapi_softc *);
static void atapi_read(struct atapi_request *, int32_t);
static void atapi_write(struct atapi_request *, int32_t);
static void atapi_timeout(struct atapi_request *request);
static int8_t *atapi_type(int32_t);
static int8_t *atapi_cmd2str(u_int8_t);
@ -135,6 +137,7 @@ atapi_attach(void *notused)
udmamode(atp->atapi_parm),
atp->atapi_parm->dmaflag);
#ifdef ATA_ENABLE_ATAPI_DMA
if (!(atp->atapi_parm->drqtype == ATAPI_DRQT_INTR) &&
!ata_dmainit(atp->controller, atp->unit,
(apiomode(atp->atapi_parm) < 0) ?
@ -145,6 +148,7 @@ atapi_attach(void *notused)
wdmamode(atp->atapi_parm),
udmamode(atp->atapi_parm)))
atp->flags |= ATAPI_F_DMA_ENABLED;
#endif
switch (atp->atapi_parm->device_type) {
#if NATAPICD > 0
@ -245,14 +249,16 @@ atapi_queue_cmd(struct atapi_softc *atp, int8_t *ccb, void *data,
TAILQ_INSERT_TAIL(&atp->controller->atapi_queue, request, chain);
if (atp->controller->active == ATA_IDLE)
ata_start(atp->controller);
splx(s);
/* if callback used, then just return, gets called from interrupt context */
if (callback)
if (callback) {
splx(s);
return 0;
}
/* wait for request to complete */
tsleep((caddr_t)request, PRIBIO, "atprq", 0);
splx(s);
#ifdef ATAPI_DEBUG
printf("atapi: phew, got back from tsleep\n");
@ -370,7 +376,7 @@ atapi_interrupt(struct atapi_request *request)
{
struct atapi_softc *atp = request->device;
int8_t **buffer = (int8_t **)&request->data;
int32_t length, reason, resid, dma_stat = 0;
int32_t length, reason, dma_stat = 0;
if (request->ccb[0] == ATAPI_REQUEST_SENSE)
*buffer = (int8_t *)&request->sense;
@ -432,25 +438,7 @@ atapi_interrupt(struct atapi_request *request)
atapi_cmd2str(atp->cmd));
goto op_finished;
}
if (request->bytecount < length) {
printf("atapi_interrupt: write data underrun %d/%d\n",
length, request->bytecount);
#ifdef ATA_16BIT_ONLY
outsw(atp->controller->ioaddr + ATA_DATA,
(void *)((uintptr_t)*buffer), length / sizeof(int16_t));
#else
outsl(atp->controller->ioaddr + ATA_DATA,
(void *)((uintptr_t)*buffer), length / sizeof(int32_t));
#endif
for (resid=request->bytecount; resid<length; resid+=sizeof(int16_t))
outw(atp->controller->ioaddr + ATA_DATA, 0);
}
else {
outsw(atp->controller->ioaddr + ATA_DATA,
(void *)((uintptr_t)*buffer), length / sizeof(int16_t));
}
request->bytecount -= length;
*buffer += length;
atapi_write(request, length);
return ATA_OP_CONTINUES;
case ATAPI_P_READ:
@ -462,27 +450,20 @@ atapi_interrupt(struct atapi_request *request)
atapi_cmd2str(atp->cmd));
goto op_finished;
}
if (request->bytecount < length) {
printf("atapi_interrupt: read data overrun %d/%d\n",
length, request->bytecount);
#ifdef ATA_16BIT_ONLY
insw(atp->controller->ioaddr + ATA_DATA,
(void *)((uintptr_t)*buffer), length / sizeof(int16_t));
#else
insl(atp->controller->ioaddr + ATA_DATA,
(void *)((uintptr_t)*buffer), length / sizeof(int32_t));
#endif
for (resid=request->bytecount; resid<length; resid+=sizeof(int16_t))
inw(atp->controller->ioaddr + ATA_DATA);
}
else {
insw(atp->controller->ioaddr + ATA_DATA,
(void *)((uintptr_t)*buffer), length / sizeof(int16_t));
}
request->bytecount -= length;
*buffer += length;
atapi_read(request, length);
return ATA_OP_CONTINUES;
case ATAPI_P_DONEDRQ:
printf("ata%d-%s: %s DONEDRQ\n",
atp->controller->lun,
(atp->unit == ATA_MASTER) ? "master" : "slave",
atapi_cmd2str(atp->cmd));
if (request->flags & A_READ)
atapi_read(request, length);
else
atapi_write(request, length);
/* FALLTHROUGH */
case ATAPI_P_ABORT:
case ATAPI_P_DONE:
if (atp->controller->status & (ATA_S_ERROR | ATA_S_DWF))
@ -590,11 +571,70 @@ atapi_dump(int8_t *label, void *data, int32_t len)
printf ("\n");
}
static void
atapi_read(struct atapi_request *request, int32_t length)
{
int8_t **buffer = (int8_t **)&request->data;
int32_t resid;
if (request->ccb[0] == ATAPI_REQUEST_SENSE)
*buffer = (int8_t *)&request->sense;
if (request->bytecount < length) {
printf("atapi_interrupt: read data overrun %d/%d\n",
length, request->bytecount);
#ifdef ATA_16BIT_ONLY
insw(request->device->controller->ioaddr + ATA_DATA,
(void *)((uintptr_t)*buffer), length / sizeof(int16_t));
#else
insl(request->device->controller->ioaddr + ATA_DATA,
(void *)((uintptr_t)*buffer), length / sizeof(int32_t));
#endif
for (resid=request->bytecount; resid<length; resid+=sizeof(int16_t))
inw(request->device->controller->ioaddr + ATA_DATA);
}
else
insw(request->device->controller->ioaddr + ATA_DATA,
(void *)((uintptr_t)*buffer), length / sizeof(int16_t));
request->bytecount -= length;
*buffer += length;
}
static void
atapi_write(struct atapi_request *request, int32_t length)
{
int8_t **buffer = (int8_t **)&request->data;
int32_t resid;
if (request->ccb[0] == ATAPI_REQUEST_SENSE)
*buffer = (int8_t *)&request->sense;
if (request->bytecount < length) {
printf("atapi_interrupt: write data underrun %d/%d\n",
length, request->bytecount);
#ifdef ATA_16BIT_ONLY
outsw(request->device->controller->ioaddr + ATA_DATA,
(void *)((uintptr_t)*buffer), length / sizeof(int16_t));
#else
outsl(request->device->controller->ioaddr + ATA_DATA,
(void *)((uintptr_t)*buffer), length / sizeof(int32_t));
#endif
for (resid=request->bytecount; resid<length; resid+=sizeof(int16_t))
outw(request->device->controller->ioaddr + ATA_DATA, 0);
}
else
outsw(request->device->controller->ioaddr + ATA_DATA,
(void *)((uintptr_t)*buffer), length / sizeof(int16_t));
request->bytecount -= length;
*buffer += length;
}
static void
atapi_timeout(struct atapi_request *request)
{
struct atapi_softc *atp = request->device;
atp->controller->running = NULL;
printf("ata%d-%s: atapi_timeout: cmd=%s - resetting\n",
atp->controller->lun, (atp->unit == ATA_MASTER) ? "master" : "slave",
atapi_cmd2str(request->ccb[0]));

View File

@ -34,8 +34,9 @@
#define ATAPI_P_READ (ATA_S_DRQ | ATA_I_IN)
#define ATAPI_P_WRITE (ATA_S_DRQ)
#define ATAPI_P_CMDOUT (ATA_S_DRQ | ATA_I_CMD)
#define ATAPI_P_DONEDRQ (ATA_S_DRQ | ATA_I_CMD | ATA_I_IN)
#define ATAPI_P_DONE (ATA_I_CMD | ATA_I_IN)
#define ATAPI_P_ABORT 0
#define ATAPI_P_DONE (ATA_I_IN | ATA_I_CMD)
/* error register bits */
#define ATAPI_E_MASK 0x0f /* error mask */