aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMark Lord <lkml@rtr.ca>2006-03-29 09:50:31 -0500
committerJeff Garzik <jeff@garzik.org>2006-03-29 19:40:24 -0500
commit63af2a5c5990d95f1e7d8795f1425fb976ea2b4b (patch)
treeea1a7dbe399b200aa6df8a826be206f84eb60ae2
parent7705a8792b0fc82fd7d4dd923724606bbfd9fb20 (diff)
[PATCH] sata_mv: three bug fixes
(1) A DMA transfer size of 0x10000 was not being written as 0x0000 in the PRDs. Fixed. (1) The DEV_IRQ interrupt cause bit happens spuriously during EDMA operation, and was not being ignored by the driver. This led to various "drive busy" errors being reported, with associated unpredictable behaviour. Fixed. (2) If a SATA or PCI interrupt was received with no outstanding command, the interrupt handler still attempted to invoke ata_qc_complete(), triggering assert()/BUG_ON() behaviour elsewhere in libata. Fixed. The driver still has issues with confusion after error-recovery, but should now be reliable in the absence of drive errors. I will be looking more into the error-handling bugs next. Signed-Off-By: Mark Lord <mlord@pobox.com> Signed-off-by: Jeff Garzik <jeff@garzik.org>
-rw-r--r--drivers/scsi/sata_mv.c41
1 files changed, 23 insertions, 18 deletions
diff --git a/drivers/scsi/sata_mv.c b/drivers/scsi/sata_mv.c
index 2770005324b4..33e387354388 100644
--- a/drivers/scsi/sata_mv.c
+++ b/drivers/scsi/sata_mv.c
@@ -915,7 +915,7 @@ static void mv_fill_sg(struct ata_queued_cmd *qc)
915 915
916 pp->sg_tbl[i].addr = cpu_to_le32(addr & 0xffffffff); 916 pp->sg_tbl[i].addr = cpu_to_le32(addr & 0xffffffff);
917 pp->sg_tbl[i].addr_hi = cpu_to_le32((addr >> 16) >> 16); 917 pp->sg_tbl[i].addr_hi = cpu_to_le32((addr >> 16) >> 16);
918 pp->sg_tbl[i].flags_size = cpu_to_le32(len); 918 pp->sg_tbl[i].flags_size = cpu_to_le32(len & 0xffff);
919 919
920 sg_len -= len; 920 sg_len -= len;
921 addr += len; 921 addr += len;
@@ -1187,7 +1187,6 @@ static void mv_host_intr(struct ata_host_set *host_set, u32 relevant,
1187{ 1187{
1188 void __iomem *mmio = host_set->mmio_base; 1188 void __iomem *mmio = host_set->mmio_base;
1189 void __iomem *hc_mmio = mv_hc_base(mmio, hc); 1189 void __iomem *hc_mmio = mv_hc_base(mmio, hc);
1190 struct ata_port *ap;
1191 struct ata_queued_cmd *qc; 1190 struct ata_queued_cmd *qc;
1192 u32 hc_irq_cause; 1191 u32 hc_irq_cause;
1193 int shift, port, port0, hard_port, handled; 1192 int shift, port, port0, hard_port, handled;
@@ -1210,25 +1209,31 @@ static void mv_host_intr(struct ata_host_set *host_set, u32 relevant,
1210 hc,relevant,hc_irq_cause); 1209 hc,relevant,hc_irq_cause);
1211 1210
1212 for (port = port0; port < port0 + MV_PORTS_PER_HC; port++) { 1211 for (port = port0; port < port0 + MV_PORTS_PER_HC; port++) {
1213 ap = host_set->ports[port]; 1212 struct ata_port *ap = host_set->ports[port];
1213 struct mv_port_priv *pp = ap->private_data;
1214 hard_port = port & MV_PORT_MASK; /* range 0-3 */ 1214 hard_port = port & MV_PORT_MASK; /* range 0-3 */
1215 handled = 0; /* ensure ata_status is set if handled++ */ 1215 handled = 0; /* ensure ata_status is set if handled++ */
1216 1216
1217 if ((CRPB_DMA_DONE << hard_port) & hc_irq_cause) { 1217 /* Note that DEV_IRQ might happen spuriously during EDMA,
1218 /* new CRPB on the queue; just one at a time until NCQ 1218 * and should be ignored in such cases. We could mask it,
1219 */ 1219 * but it's pretty rare and may not be worth the overhead.
1220 ata_status = mv_get_crpb_status(ap); 1220 */
1221 handled++; 1221 if (pp->pp_flags & MV_PP_FLAG_EDMA_EN) {
1222 } else if ((DEV_IRQ << hard_port) & hc_irq_cause) { 1222 /* EDMA: check for response queue interrupt */
1223 /* received ATA IRQ; read the status reg to clear INTRQ 1223 if ((CRPB_DMA_DONE << hard_port) & hc_irq_cause) {
1224 */ 1224 ata_status = mv_get_crpb_status(ap);
1225 ata_status = readb((void __iomem *) 1225 handled = 1;
1226 }
1227 } else {
1228 /* PIO: check for device (drive) interrupt */
1229 if ((DEV_IRQ << hard_port) & hc_irq_cause) {
1230 ata_status = readb((void __iomem *)
1226 ap->ioaddr.status_addr); 1231 ap->ioaddr.status_addr);
1227 handled++; 1232 handled = 1;
1233 }
1228 } 1234 }
1229 1235
1230 if (ap && 1236 if (ap->flags & (ATA_FLAG_PORT_DISABLED | ATA_FLAG_NOINTR))
1231 (ap->flags & (ATA_FLAG_PORT_DISABLED | ATA_FLAG_NOINTR)))
1232 continue; 1237 continue;
1233 1238
1234 err_mask = ac_err_mask(ata_status); 1239 err_mask = ac_err_mask(ata_status);
@@ -1240,12 +1245,12 @@ static void mv_host_intr(struct ata_host_set *host_set, u32 relevant,
1240 if ((PORT0_ERR << shift) & relevant) { 1245 if ((PORT0_ERR << shift) & relevant) {
1241 mv_err_intr(ap); 1246 mv_err_intr(ap);
1242 err_mask |= AC_ERR_OTHER; 1247 err_mask |= AC_ERR_OTHER;
1243 handled++; 1248 handled = 1;
1244 } 1249 }
1245 1250
1246 if (handled && ap) { 1251 if (handled) {
1247 qc = ata_qc_from_tag(ap, ap->active_tag); 1252 qc = ata_qc_from_tag(ap, ap->active_tag);
1248 if (NULL != qc) { 1253 if (qc && (qc->flags & ATA_QCFLAG_ACTIVE)) {
1249 VPRINTK("port %u IRQ found for qc, " 1254 VPRINTK("port %u IRQ found for qc, "
1250 "ata_status 0x%x\n", port,ata_status); 1255 "ata_status 0x%x\n", port,ata_status);
1251 /* mark qc status appropriately */ 1256 /* mark qc status appropriately */