diff options
author | Brett Russ <russb@emc.com> | 2005-10-05 17:08:42 -0400 |
---|---|---|
committer | Jeff Garzik <jgarzik@pobox.com> | 2005-10-05 17:16:52 -0400 |
commit | afb0edd922c7ed6e73678730921dfcccebec17e8 (patch) | |
tree | cbe655b60569c7a649da8f1b8d6527aa1fca8322 /drivers/scsi/sata_mv.c | |
parent | a939c9631527053aa38aa8795a6f7203c7f20b69 (diff) |
[PATCH] libata: Marvell spinlock fixes and simplification
This should fix up lockups that people were seeing due to
improper spinlock placement. Also, the start/stop DMA routines put
guarded trust in the cached state of DMA.
Signed-off-by: Brett Russ <russb@emc.com>
Signed-off-by: Jeff Garzik <jgarzik@pobox.com>
Diffstat (limited to 'drivers/scsi/sata_mv.c')
-rw-r--r-- | drivers/scsi/sata_mv.c | 58 |
1 files changed, 24 insertions, 34 deletions
diff --git a/drivers/scsi/sata_mv.c b/drivers/scsi/sata_mv.c index ecda7df21142..c3084f8b3ee7 100644 --- a/drivers/scsi/sata_mv.c +++ b/drivers/scsi/sata_mv.c | |||
@@ -35,7 +35,7 @@ | |||
35 | #include <asm/io.h> | 35 | #include <asm/io.h> |
36 | 36 | ||
37 | #define DRV_NAME "sata_mv" | 37 | #define DRV_NAME "sata_mv" |
38 | #define DRV_VERSION "0.22" | 38 | #define DRV_VERSION "0.23" |
39 | 39 | ||
40 | enum { | 40 | enum { |
41 | /* BAR's are enumerated in terms of pci_resource_start() terms */ | 41 | /* BAR's are enumerated in terms of pci_resource_start() terms */ |
@@ -406,40 +406,30 @@ static void mv_irq_clear(struct ata_port *ap) | |||
406 | { | 406 | { |
407 | } | 407 | } |
408 | 408 | ||
409 | static void mv_start_dma(void __iomem *base, struct mv_port_priv *pp, | 409 | static void mv_start_dma(void __iomem *base, struct mv_port_priv *pp) |
410 | struct ata_port *ap) | ||
411 | { | 410 | { |
412 | unsigned long flags; | 411 | if (!(MV_PP_FLAG_EDMA_EN & pp->pp_flags)) { |
413 | 412 | writelfl(EDMA_EN, base + EDMA_CMD_OFS); | |
414 | spin_lock_irqsave(&ap->host_set->lock, flags); | 413 | pp->pp_flags |= MV_PP_FLAG_EDMA_EN; |
415 | 414 | } | |
416 | writelfl(EDMA_EN, base + EDMA_CMD_OFS); | 415 | assert(EDMA_EN & readl(base + EDMA_CMD_OFS)); |
417 | pp->pp_flags |= MV_PP_FLAG_EDMA_EN; | ||
418 | |||
419 | spin_unlock_irqrestore(&ap->host_set->lock, flags); | ||
420 | } | 416 | } |
421 | 417 | ||
422 | static void mv_stop_dma(struct ata_port *ap) | 418 | static void mv_stop_dma(struct ata_port *ap) |
423 | { | 419 | { |
424 | void __iomem *port_mmio = mv_ap_base(ap); | 420 | void __iomem *port_mmio = mv_ap_base(ap); |
425 | struct mv_port_priv *pp = ap->private_data; | 421 | struct mv_port_priv *pp = ap->private_data; |
426 | unsigned long flags; | ||
427 | u32 reg; | 422 | u32 reg; |
428 | int i; | 423 | int i; |
429 | 424 | ||
430 | spin_lock_irqsave(&ap->host_set->lock, flags); | 425 | if (MV_PP_FLAG_EDMA_EN & pp->pp_flags) { |
431 | 426 | /* Disable EDMA if active. The disable bit auto clears. | |
432 | if (!(MV_PP_FLAG_EDMA_DS_ACT & pp->pp_flags) && | ||
433 | ((MV_PP_FLAG_EDMA_EN & pp->pp_flags) || | ||
434 | (EDMA_EN & readl(port_mmio + EDMA_CMD_OFS)))) { | ||
435 | /* Disable EDMA if we're not already trying to disable it | ||
436 | * and it is currently active. The disable bit auto clears. | ||
437 | */ | 427 | */ |
438 | pp->pp_flags |= MV_PP_FLAG_EDMA_DS_ACT; | ||
439 | writelfl(EDMA_DS, port_mmio + EDMA_CMD_OFS); | 428 | writelfl(EDMA_DS, port_mmio + EDMA_CMD_OFS); |
440 | pp->pp_flags &= ~MV_PP_FLAG_EDMA_EN; | 429 | pp->pp_flags &= ~MV_PP_FLAG_EDMA_EN; |
441 | } | 430 | } else { |
442 | spin_unlock_irqrestore(&ap->host_set->lock, flags); | 431 | assert(!(EDMA_EN & readl(port_mmio + EDMA_CMD_OFS))); |
432 | } | ||
443 | 433 | ||
444 | /* now properly wait for the eDMA to stop */ | 434 | /* now properly wait for the eDMA to stop */ |
445 | for (i = 1000; i > 0; i--) { | 435 | for (i = 1000; i > 0; i--) { |
@@ -450,12 +440,9 @@ static void mv_stop_dma(struct ata_port *ap) | |||
450 | udelay(100); | 440 | udelay(100); |
451 | } | 441 | } |
452 | 442 | ||
453 | spin_lock_irqsave(&ap->host_set->lock, flags); | ||
454 | pp->pp_flags &= ~MV_PP_FLAG_EDMA_DS_ACT; | ||
455 | spin_unlock_irqrestore(&ap->host_set->lock, flags); | ||
456 | |||
457 | if (EDMA_EN & reg) { | 443 | if (EDMA_EN & reg) { |
458 | printk(KERN_ERR "ata%u: Unable to stop eDMA\n", ap->id); | 444 | printk(KERN_ERR "ata%u: Unable to stop eDMA\n", ap->id); |
445 | /* FIXME: Consider doing a reset here to recover */ | ||
459 | } | 446 | } |
460 | } | 447 | } |
461 | 448 | ||
@@ -716,8 +703,11 @@ static void mv_port_stop(struct ata_port *ap) | |||
716 | { | 703 | { |
717 | struct device *dev = ap->host_set->dev; | 704 | struct device *dev = ap->host_set->dev; |
718 | struct mv_port_priv *pp = ap->private_data; | 705 | struct mv_port_priv *pp = ap->private_data; |
706 | unsigned long flags; | ||
719 | 707 | ||
708 | spin_lock_irqsave(&ap->host_set->lock, flags); | ||
720 | mv_stop_dma(ap); | 709 | mv_stop_dma(ap); |
710 | spin_unlock_irqrestore(&ap->host_set->lock, flags); | ||
721 | 711 | ||
722 | ap->private_data = NULL; | 712 | ap->private_data = NULL; |
723 | dma_free_coherent(dev, MV_PORT_PRIV_DMA_SZ, pp->crpb, pp->crpb_dma); | 713 | dma_free_coherent(dev, MV_PORT_PRIV_DMA_SZ, pp->crpb, pp->crpb_dma); |
@@ -867,11 +857,7 @@ static int mv_qc_issue(struct ata_queued_cmd *qc) | |||
867 | 857 | ||
868 | mv_inc_q_index(&pp->req_producer); /* now incr producer index */ | 858 | mv_inc_q_index(&pp->req_producer); /* now incr producer index */ |
869 | 859 | ||
870 | if (!(MV_PP_FLAG_EDMA_EN & pp->pp_flags)) { | 860 | mv_start_dma(port_mmio, pp); |
871 | /* turn on EDMA if not already on */ | ||
872 | mv_start_dma(port_mmio, pp, qc->ap); | ||
873 | } | ||
874 | assert(EDMA_EN & readl(port_mmio + EDMA_CMD_OFS)); | ||
875 | 861 | ||
876 | /* and write the request in pointer to kick the EDMA to life */ | 862 | /* and write the request in pointer to kick the EDMA to life */ |
877 | in_ptr &= EDMA_REQ_Q_BASE_LO_MASK; | 863 | in_ptr &= EDMA_REQ_Q_BASE_LO_MASK; |
@@ -921,8 +907,12 @@ static void mv_err_intr(struct ata_port *ap) | |||
921 | serr = scr_read(ap, SCR_ERROR); | 907 | serr = scr_read(ap, SCR_ERROR); |
922 | scr_write_flush(ap, SCR_ERROR, serr); | 908 | scr_write_flush(ap, SCR_ERROR, serr); |
923 | } | 909 | } |
924 | DPRINTK("port %u error; EDMA err cause: 0x%08x SERR: 0x%08x\n", | 910 | if (EDMA_ERR_SELF_DIS & edma_err_cause) { |
925 | ap->port_no, edma_err_cause, serr); | 911 | struct mv_port_priv *pp = ap->private_data; |
912 | pp->pp_flags &= ~MV_PP_FLAG_EDMA_EN; | ||
913 | } | ||
914 | DPRINTK(KERN_ERR "ata%u: port error; EDMA err cause: 0x%08x " | ||
915 | "SERR: 0x%08x\n", ap->id, edma_err_cause, serr); | ||
926 | 916 | ||
927 | /* Clear EDMA now that SERR cleanup done */ | 917 | /* Clear EDMA now that SERR cleanup done */ |
928 | writelfl(0, port_mmio + EDMA_ERR_IRQ_CAUSE_OFS); | 918 | writelfl(0, port_mmio + EDMA_ERR_IRQ_CAUSE_OFS); |
@@ -1034,7 +1024,7 @@ static irqreturn_t mv_interrupt(int irq, void *dev_instance, | |||
1034 | printk(KERN_ERR DRV_NAME ": PCI ERROR; PCI IRQ cause=0x%08x\n", | 1024 | printk(KERN_ERR DRV_NAME ": PCI ERROR; PCI IRQ cause=0x%08x\n", |
1035 | readl(mmio + PCI_IRQ_CAUSE_OFS)); | 1025 | readl(mmio + PCI_IRQ_CAUSE_OFS)); |
1036 | 1026 | ||
1037 | VPRINTK("All regs @ PCI error\n"); | 1027 | DPRINTK("All regs @ PCI error\n"); |
1038 | mv_dump_all_regs(mmio, -1, to_pci_dev(host_set->dev)); | 1028 | mv_dump_all_regs(mmio, -1, to_pci_dev(host_set->dev)); |
1039 | 1029 | ||
1040 | writelfl(0, mmio + PCI_IRQ_CAUSE_OFS); | 1030 | writelfl(0, mmio + PCI_IRQ_CAUSE_OFS); |