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 | ||
