aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTejun Heo <htejun@gmail.com>2006-05-31 05:27:55 -0400
committerTejun Heo <htejun@gmail.com>2006-05-31 05:27:55 -0400
commitcbe88fbc72d9e1aa4a6f994cb6e19fa08ae5a0ba (patch)
tree117fce53d60b1f08346ee9fa64c6b0c6fe72b4bf
parent20888d83687d5cb374cdb5b0afa746ab79666f4e (diff)
[PATCH] sata_sil: new interrupt handler
The DMA complete bit of these controllers reflects ATA IRQ status while no DMA command is in progress. So, we can tell whether the controller is raising an interrupt or not in deterministic manner. This patch gives sata_sil its own interrupt handler which behaves much better than the original one in terms of error detection and handling. This change is also necessary for later hotplug support. Further improvements are possible, in both 2 and 4 ports versions, we can get all status with only one readl and using custom bmdma operations can further cut down register accesses. Signed-off-by: Tejun Heo <htejun@gmail.com>
-rw-r--r--drivers/scsi/sata_sil.c92
1 files changed, 91 insertions, 1 deletions
diff --git a/drivers/scsi/sata_sil.c b/drivers/scsi/sata_sil.c
index f926883fd16c..03c1d1b1a04c 100644
--- a/drivers/scsi/sata_sil.c
+++ b/drivers/scsi/sata_sil.c
@@ -111,6 +111,8 @@ static void sil_dev_config(struct ata_port *ap, struct ata_device *dev);
111static u32 sil_scr_read (struct ata_port *ap, unsigned int sc_reg); 111static u32 sil_scr_read (struct ata_port *ap, unsigned int sc_reg);
112static void sil_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val); 112static void sil_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val);
113static void sil_post_set_mode (struct ata_port *ap); 113static void sil_post_set_mode (struct ata_port *ap);
114static irqreturn_t sil_interrupt(int irq, void *dev_instance,
115 struct pt_regs *regs);
114static void sil_freeze(struct ata_port *ap); 116static void sil_freeze(struct ata_port *ap);
115static void sil_thaw(struct ata_port *ap); 117static void sil_thaw(struct ata_port *ap);
116 118
@@ -196,7 +198,7 @@ static const struct ata_port_operations sil_ops = {
196 .thaw = sil_thaw, 198 .thaw = sil_thaw,
197 .error_handler = ata_bmdma_error_handler, 199 .error_handler = ata_bmdma_error_handler,
198 .post_internal_cmd = ata_bmdma_post_internal_cmd, 200 .post_internal_cmd = ata_bmdma_post_internal_cmd,
199 .irq_handler = ata_interrupt, 201 .irq_handler = sil_interrupt,
200 .irq_clear = ata_bmdma_irq_clear, 202 .irq_clear = ata_bmdma_irq_clear,
201 .scr_read = sil_scr_read, 203 .scr_read = sil_scr_read,
202 .scr_write = sil_scr_write, 204 .scr_write = sil_scr_write,
@@ -336,6 +338,94 @@ static void sil_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val)
336 writel(val, mmio); 338 writel(val, mmio);
337} 339}
338 340
341static void sil_host_intr(struct ata_port *ap, u32 bmdma2)
342{
343 struct ata_queued_cmd *qc = ata_qc_from_tag(ap, ap->active_tag);
344 u8 status;
345
346 if (unlikely(!qc || qc->tf.ctl & ATA_NIEN))
347 goto freeze;
348
349 /* Check whether we are expecting interrupt in this state */
350 switch (ap->hsm_task_state) {
351 case HSM_ST_FIRST:
352 /* Some pre-ATAPI-4 devices assert INTRQ
353 * at this state when ready to receive CDB.
354 */
355
356 /* Check the ATA_DFLAG_CDB_INTR flag is enough here.
357 * The flag was turned on only for atapi devices.
358 * No need to check is_atapi_taskfile(&qc->tf) again.
359 */
360 if (!(qc->dev->flags & ATA_DFLAG_CDB_INTR))
361 goto err_hsm;
362 break;
363 case HSM_ST_LAST:
364 if (qc->tf.protocol == ATA_PROT_DMA ||
365 qc->tf.protocol == ATA_PROT_ATAPI_DMA) {
366 /* clear DMA-Start bit */
367 ap->ops->bmdma_stop(qc);
368
369 if (bmdma2 & SIL_DMA_ERROR) {
370 qc->err_mask |= AC_ERR_HOST_BUS;
371 ap->hsm_task_state = HSM_ST_ERR;
372 }
373 }
374 break;
375 case HSM_ST:
376 break;
377 default:
378 goto err_hsm;
379 }
380
381 /* check main status, clearing INTRQ */
382 status = ata_chk_status(ap);
383 if (unlikely(status & ATA_BUSY))
384 goto err_hsm;
385
386 /* ack bmdma irq events */
387 ata_bmdma_irq_clear(ap);
388
389 /* kick HSM in the ass */
390 ata_hsm_move(ap, qc, status, 0);
391
392 return;
393
394 err_hsm:
395 qc->err_mask |= AC_ERR_HSM;
396 freeze:
397 ata_port_freeze(ap);
398}
399
400static irqreturn_t sil_interrupt(int irq, void *dev_instance,
401 struct pt_regs *regs)
402{
403 struct ata_host_set *host_set = dev_instance;
404 void __iomem *mmio_base = host_set->mmio_base;
405 int handled = 0;
406 int i;
407
408 spin_lock(&host_set->lock);
409
410 for (i = 0; i < host_set->n_ports; i++) {
411 struct ata_port *ap = host_set->ports[i];
412 u32 bmdma2 = readl(mmio_base + sil_port[ap->port_no].bmdma2);
413
414 if (unlikely(!ap || ap->flags & ATA_FLAG_DISABLED))
415 continue;
416
417 if (!(bmdma2 & SIL_DMA_COMPLETE))
418 continue;
419
420 sil_host_intr(ap, bmdma2);
421 handled = 1;
422 }
423
424 spin_unlock(&host_set->lock);
425
426 return IRQ_RETVAL(handled);
427}
428
339static void sil_freeze(struct ata_port *ap) 429static void sil_freeze(struct ata_port *ap)
340{ 430{
341 void __iomem *mmio_base = ap->host_set->mmio_base; 431 void __iomem *mmio_base = ap->host_set->mmio_base;