diff options
author | Tejun Heo <htejun@gmail.com> | 2006-05-31 05:28:18 -0400 |
---|---|---|
committer | Tejun Heo <htejun@gmail.com> | 2006-05-31 05:28:18 -0400 |
commit | 4296971dd36e2c2deae0826305f591480223af88 (patch) | |
tree | bd3cce15c252cea18869b39da231d542a6255e59 /drivers | |
parent | e573890b00426189e1e223967a2c46fb758bf06e (diff) |
[PATCH] ahci: convert to new probing mechanism and add hotplug support
Convert to new probing mechanism and add hotplug support by enabling
PORT_IRQ_PHYRDY, marking ehi for hotplug and scheduling EH on
CONNECT/PHYRDY interrupts.
Unfortunately, ahci cannot reliably wait for the first D2H FIS after
hotplug. It sometimes succeeds but times out more often than not, so
ATA_FLAG_SKIP_D2H_BSY is used.
This patch also fixes ahci_hardreset() such that D2H Register FIS RX
area is cleared before issuing COMRESET. Without this,
ata_busy_sleep() after COMRESET might prematually finish if the
previous TF contains DRDY && !BSY.
Signed-off-by: Tejun Heo <htejun@gmail.com>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/scsi/ahci.c | 47 |
1 files changed, 27 insertions, 20 deletions
diff --git a/drivers/scsi/ahci.c b/drivers/scsi/ahci.c index 60f455bf3696..e261b37c2e48 100644 --- a/drivers/scsi/ahci.c +++ b/drivers/scsi/ahci.c | |||
@@ -136,6 +136,7 @@ enum { | |||
136 | PORT_IRQ_FREEZE = PORT_IRQ_HBUS_ERR | | 136 | PORT_IRQ_FREEZE = PORT_IRQ_HBUS_ERR | |
137 | PORT_IRQ_IF_ERR | | 137 | PORT_IRQ_IF_ERR | |
138 | PORT_IRQ_CONNECT | | 138 | PORT_IRQ_CONNECT | |
139 | PORT_IRQ_PHYRDY | | ||
139 | PORT_IRQ_UNK_FIS, | 140 | PORT_IRQ_UNK_FIS, |
140 | PORT_IRQ_ERROR = PORT_IRQ_FREEZE | | 141 | PORT_IRQ_ERROR = PORT_IRQ_FREEZE | |
141 | PORT_IRQ_TF_ERR | | 142 | PORT_IRQ_TF_ERR | |
@@ -200,7 +201,6 @@ static void ahci_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val); | |||
200 | static int ahci_init_one (struct pci_dev *pdev, const struct pci_device_id *ent); | 201 | static int ahci_init_one (struct pci_dev *pdev, const struct pci_device_id *ent); |
201 | static unsigned int ahci_qc_issue(struct ata_queued_cmd *qc); | 202 | static unsigned int ahci_qc_issue(struct ata_queued_cmd *qc); |
202 | static irqreturn_t ahci_interrupt (int irq, void *dev_instance, struct pt_regs *regs); | 203 | static irqreturn_t ahci_interrupt (int irq, void *dev_instance, struct pt_regs *regs); |
203 | static int ahci_probe_reset(struct ata_port *ap, unsigned int *classes); | ||
204 | static void ahci_irq_clear(struct ata_port *ap); | 204 | static void ahci_irq_clear(struct ata_port *ap); |
205 | static int ahci_port_start(struct ata_port *ap); | 205 | static int ahci_port_start(struct ata_port *ap); |
206 | static void ahci_port_stop(struct ata_port *ap); | 206 | static void ahci_port_stop(struct ata_port *ap); |
@@ -241,8 +241,6 @@ static const struct ata_port_operations ahci_ops = { | |||
241 | 241 | ||
242 | .tf_read = ahci_tf_read, | 242 | .tf_read = ahci_tf_read, |
243 | 243 | ||
244 | .probe_reset = ahci_probe_reset, | ||
245 | |||
246 | .qc_prep = ahci_qc_prep, | 244 | .qc_prep = ahci_qc_prep, |
247 | .qc_issue = ahci_qc_issue, | 245 | .qc_issue = ahci_qc_issue, |
248 | 246 | ||
@@ -267,7 +265,8 @@ static const struct ata_port_info ahci_port_info[] = { | |||
267 | { | 265 | { |
268 | .sht = &ahci_sht, | 266 | .sht = &ahci_sht, |
269 | .host_flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | | 267 | .host_flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | |
270 | ATA_FLAG_MMIO | ATA_FLAG_PIO_DMA, | 268 | ATA_FLAG_MMIO | ATA_FLAG_PIO_DMA | |
269 | ATA_FLAG_SKIP_D2H_BSY, | ||
271 | .pio_mask = 0x1f, /* pio0-4 */ | 270 | .pio_mask = 0x1f, /* pio0-4 */ |
272 | .udma_mask = 0x7f, /* udma0-6 ; FIXME */ | 271 | .udma_mask = 0x7f, /* udma0-6 ; FIXME */ |
273 | .port_ops = &ahci_ops, | 272 | .port_ops = &ahci_ops, |
@@ -277,6 +276,7 @@ static const struct ata_port_info ahci_port_info[] = { | |||
277 | .sht = &ahci_sht, | 276 | .sht = &ahci_sht, |
278 | .host_flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | | 277 | .host_flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | |
279 | ATA_FLAG_MMIO | ATA_FLAG_PIO_DMA | | 278 | ATA_FLAG_MMIO | ATA_FLAG_PIO_DMA | |
279 | ATA_FLAG_SKIP_D2H_BSY | | ||
280 | AHCI_FLAG_RESET_NEEDS_CLO, | 280 | AHCI_FLAG_RESET_NEEDS_CLO, |
281 | .pio_mask = 0x1f, /* pio0-4 */ | 281 | .pio_mask = 0x1f, /* pio0-4 */ |
282 | .udma_mask = 0x7f, /* udma0-6 ; FIXME */ | 282 | .udma_mask = 0x7f, /* udma0-6 ; FIXME */ |
@@ -569,6 +569,17 @@ static int ahci_clo(struct ata_port *ap) | |||
569 | return 0; | 569 | return 0; |
570 | } | 570 | } |
571 | 571 | ||
572 | static int ahci_prereset(struct ata_port *ap) | ||
573 | { | ||
574 | if ((ap->flags & AHCI_FLAG_RESET_NEEDS_CLO) && | ||
575 | (ata_busy_wait(ap, ATA_BUSY, 1000) & ATA_BUSY)) { | ||
576 | /* ATA_BUSY hasn't cleared, so send a CLO */ | ||
577 | ahci_clo(ap); | ||
578 | } | ||
579 | |||
580 | return ata_std_prereset(ap); | ||
581 | } | ||
582 | |||
572 | static int ahci_softreset(struct ata_port *ap, unsigned int *class) | 583 | static int ahci_softreset(struct ata_port *ap, unsigned int *class) |
573 | { | 584 | { |
574 | struct ahci_port_priv *pp = ap->private_data; | 585 | struct ahci_port_priv *pp = ap->private_data; |
@@ -678,12 +689,22 @@ static int ahci_softreset(struct ata_port *ap, unsigned int *class) | |||
678 | 689 | ||
679 | static int ahci_hardreset(struct ata_port *ap, unsigned int *class) | 690 | static int ahci_hardreset(struct ata_port *ap, unsigned int *class) |
680 | { | 691 | { |
692 | struct ahci_port_priv *pp = ap->private_data; | ||
693 | u8 *d2h_fis = pp->rx_fis + RX_FIS_D2H_REG; | ||
694 | struct ata_taskfile tf; | ||
681 | int rc; | 695 | int rc; |
682 | 696 | ||
683 | DPRINTK("ENTER\n"); | 697 | DPRINTK("ENTER\n"); |
684 | 698 | ||
685 | ahci_stop_engine(ap); | 699 | ahci_stop_engine(ap); |
700 | |||
701 | /* clear D2H reception area to properly wait for D2H FIS */ | ||
702 | ata_tf_init(ap->device, &tf); | ||
703 | tf.command = 0xff; | ||
704 | ata_tf_to_fis(&tf, d2h_fis, 0); | ||
705 | |||
686 | rc = sata_std_hardreset(ap, class); | 706 | rc = sata_std_hardreset(ap, class); |
707 | |||
687 | ahci_start_engine(ap); | 708 | ahci_start_engine(ap); |
688 | 709 | ||
689 | if (rc == 0 && ata_port_online(ap)) | 710 | if (rc == 0 && ata_port_online(ap)) |
@@ -714,19 +735,6 @@ static void ahci_postreset(struct ata_port *ap, unsigned int *class) | |||
714 | } | 735 | } |
715 | } | 736 | } |
716 | 737 | ||
717 | static int ahci_probe_reset(struct ata_port *ap, unsigned int *classes) | ||
718 | { | ||
719 | if ((ap->flags & AHCI_FLAG_RESET_NEEDS_CLO) && | ||
720 | (ata_busy_wait(ap, ATA_BUSY, 1000) & ATA_BUSY)) { | ||
721 | /* ATA_BUSY hasn't cleared, so send a CLO */ | ||
722 | ahci_clo(ap); | ||
723 | } | ||
724 | |||
725 | return ata_drive_probe_reset(ap, ata_std_probeinit, | ||
726 | ahci_softreset, ahci_hardreset, | ||
727 | ahci_postreset, classes); | ||
728 | } | ||
729 | |||
730 | static u8 ahci_check_status(struct ata_port *ap) | 738 | static u8 ahci_check_status(struct ata_port *ap) |
731 | { | 739 | { |
732 | void __iomem *mmio = (void __iomem *) ap->ioaddr.cmd_addr; | 740 | void __iomem *mmio = (void __iomem *) ap->ioaddr.cmd_addr; |
@@ -839,8 +847,7 @@ static void ahci_error_intr(struct ata_port *ap, u32 irq_stat) | |||
839 | } | 847 | } |
840 | 848 | ||
841 | if (irq_stat & (PORT_IRQ_CONNECT | PORT_IRQ_PHYRDY)) { | 849 | if (irq_stat & (PORT_IRQ_CONNECT | PORT_IRQ_PHYRDY)) { |
842 | err_mask |= AC_ERR_ATA_BUS; | 850 | ata_ehi_hotplugged(ehi); |
843 | action |= ATA_EH_SOFTRESET; | ||
844 | ata_ehi_push_desc(ehi, ", %s", irq_stat & PORT_IRQ_CONNECT ? | 851 | ata_ehi_push_desc(ehi, ", %s", irq_stat & PORT_IRQ_CONNECT ? |
845 | "connection status changed" : "PHY RDY changed"); | 852 | "connection status changed" : "PHY RDY changed"); |
846 | } | 853 | } |
@@ -1027,7 +1034,7 @@ static void ahci_error_handler(struct ata_port *ap) | |||
1027 | } | 1034 | } |
1028 | 1035 | ||
1029 | /* perform recovery */ | 1036 | /* perform recovery */ |
1030 | ata_do_eh(ap, ata_std_prereset, ahci_softreset, ahci_hardreset, | 1037 | ata_do_eh(ap, ahci_prereset, ahci_softreset, ahci_hardreset, |
1031 | ahci_postreset); | 1038 | ahci_postreset); |
1032 | } | 1039 | } |
1033 | 1040 | ||