diff options
author | Bastiaan Jacques <b.jacques@planet.nl> | 2006-04-17 08:17:59 -0400 |
---|---|---|
committer | Jeff Garzik <jeff@garzik.org> | 2006-04-20 18:37:08 -0400 |
commit | bf2af2a2027e52b653882fbca840620e896ae081 (patch) | |
tree | e42ce1ac34058c1990d929f38a6e805dd6edb602 /drivers/scsi | |
parent | 857c68f733eea07f11a061caea43a38fed61adb7 (diff) |
[PATCH] ahci: add support for VIA VT8251
Adds AHCI support for the VIA VT8251.
Includes a workaround for a hardware bug which requires a Command List
Override before softreset.
Signed-off-by: Bastiaan Jacques <b.jacques@planet.nl>
Signed-off-by: Jeff Garzik <jeff@garzik.org>
Diffstat (limited to 'drivers/scsi')
-rw-r--r-- | drivers/scsi/ahci.c | 64 |
1 files changed, 49 insertions, 15 deletions
diff --git a/drivers/scsi/ahci.c b/drivers/scsi/ahci.c index 1b8429cb3c9..d23f00230a7 100644 --- a/drivers/scsi/ahci.c +++ b/drivers/scsi/ahci.c | |||
@@ -73,6 +73,7 @@ enum { | |||
73 | RX_FIS_D2H_REG = 0x40, /* offset of D2H Register FIS data */ | 73 | RX_FIS_D2H_REG = 0x40, /* offset of D2H Register FIS data */ |
74 | 74 | ||
75 | board_ahci = 0, | 75 | board_ahci = 0, |
76 | board_ahci_vt8251 = 1, | ||
76 | 77 | ||
77 | /* global controller registers */ | 78 | /* global controller registers */ |
78 | HOST_CAP = 0x00, /* host capabilities */ | 79 | HOST_CAP = 0x00, /* host capabilities */ |
@@ -153,6 +154,9 @@ enum { | |||
153 | 154 | ||
154 | /* hpriv->flags bits */ | 155 | /* hpriv->flags bits */ |
155 | AHCI_FLAG_MSI = (1 << 0), | 156 | AHCI_FLAG_MSI = (1 << 0), |
157 | |||
158 | /* ap->flags bits */ | ||
159 | AHCI_FLAG_RESET_NEEDS_CLO = (1 << 24), | ||
156 | }; | 160 | }; |
157 | 161 | ||
158 | struct ahci_cmd_hdr { | 162 | struct ahci_cmd_hdr { |
@@ -255,6 +259,16 @@ static const struct ata_port_info ahci_port_info[] = { | |||
255 | .udma_mask = 0x7f, /* udma0-6 ; FIXME */ | 259 | .udma_mask = 0x7f, /* udma0-6 ; FIXME */ |
256 | .port_ops = &ahci_ops, | 260 | .port_ops = &ahci_ops, |
257 | }, | 261 | }, |
262 | /* board_ahci_vt8251 */ | ||
263 | { | ||
264 | .sht = &ahci_sht, | ||
265 | .host_flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | | ||
266 | ATA_FLAG_MMIO | ATA_FLAG_PIO_DMA | | ||
267 | AHCI_FLAG_RESET_NEEDS_CLO, | ||
268 | .pio_mask = 0x1f, /* pio0-4 */ | ||
269 | .udma_mask = 0x7f, /* udma0-6 ; FIXME */ | ||
270 | .port_ops = &ahci_ops, | ||
271 | }, | ||
258 | }; | 272 | }; |
259 | 273 | ||
260 | static const struct pci_device_id ahci_pci_tbl[] = { | 274 | static const struct pci_device_id ahci_pci_tbl[] = { |
@@ -296,6 +310,8 @@ static const struct pci_device_id ahci_pci_tbl[] = { | |||
296 | board_ahci }, /* ATI SB600 non-raid */ | 310 | board_ahci }, /* ATI SB600 non-raid */ |
297 | { PCI_VENDOR_ID_ATI, 0x4381, PCI_ANY_ID, PCI_ANY_ID, 0, 0, | 311 | { PCI_VENDOR_ID_ATI, 0x4381, PCI_ANY_ID, PCI_ANY_ID, 0, 0, |
298 | board_ahci }, /* ATI SB600 raid */ | 312 | board_ahci }, /* ATI SB600 raid */ |
313 | { PCI_VENDOR_ID_VIA, 0x3349, PCI_ANY_ID, PCI_ANY_ID, 0, 0, | ||
314 | board_ahci_vt8251 }, /* VIA VT8251 */ | ||
299 | { } /* terminate list */ | 315 | { } /* terminate list */ |
300 | }; | 316 | }; |
301 | 317 | ||
@@ -516,9 +532,29 @@ static void ahci_fill_cmd_slot(struct ahci_port_priv *pp, u32 opts) | |||
516 | pp->cmd_slot[0].tbl_addr_hi = cpu_to_le32((pp->cmd_tbl_dma >> 16) >> 16); | 532 | pp->cmd_slot[0].tbl_addr_hi = cpu_to_le32((pp->cmd_tbl_dma >> 16) >> 16); |
517 | } | 533 | } |
518 | 534 | ||
519 | static int ahci_softreset(struct ata_port *ap, unsigned int *class) | 535 | static int ahci_clo(struct ata_port *ap) |
520 | { | 536 | { |
537 | void __iomem *port_mmio = (void __iomem *) ap->ioaddr.cmd_addr; | ||
521 | struct ahci_host_priv *hpriv = ap->host_set->private_data; | 538 | struct ahci_host_priv *hpriv = ap->host_set->private_data; |
539 | u32 tmp; | ||
540 | |||
541 | if (!(hpriv->cap & HOST_CAP_CLO)) | ||
542 | return -EOPNOTSUPP; | ||
543 | |||
544 | tmp = readl(port_mmio + PORT_CMD); | ||
545 | tmp |= PORT_CMD_CLO; | ||
546 | writel(tmp, port_mmio + PORT_CMD); | ||
547 | |||
548 | tmp = ata_wait_register(port_mmio + PORT_CMD, | ||
549 | PORT_CMD_CLO, PORT_CMD_CLO, 1, 500); | ||
550 | if (tmp & PORT_CMD_CLO) | ||
551 | return -EIO; | ||
552 | |||
553 | return 0; | ||
554 | } | ||
555 | |||
556 | static int ahci_softreset(struct ata_port *ap, unsigned int *class) | ||
557 | { | ||
522 | struct ahci_port_priv *pp = ap->private_data; | 558 | struct ahci_port_priv *pp = ap->private_data; |
523 | void __iomem *mmio = ap->host_set->mmio_base; | 559 | void __iomem *mmio = ap->host_set->mmio_base; |
524 | void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no); | 560 | void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no); |
@@ -547,21 +583,13 @@ static int ahci_softreset(struct ata_port *ap, unsigned int *class) | |||
547 | /* check BUSY/DRQ, perform Command List Override if necessary */ | 583 | /* check BUSY/DRQ, perform Command List Override if necessary */ |
548 | ahci_tf_read(ap, &tf); | 584 | ahci_tf_read(ap, &tf); |
549 | if (tf.command & (ATA_BUSY | ATA_DRQ)) { | 585 | if (tf.command & (ATA_BUSY | ATA_DRQ)) { |
550 | if (!(hpriv->cap & HOST_CAP_CLO)) { | 586 | rc = ahci_clo(ap); |
551 | rc = -EIO; | ||
552 | reason = "port busy but no CLO"; | ||
553 | goto fail_restart; | ||
554 | } | ||
555 | |||
556 | tmp = readl(port_mmio + PORT_CMD); | ||
557 | tmp |= PORT_CMD_CLO; | ||
558 | writel(tmp, port_mmio + PORT_CMD); | ||
559 | 587 | ||
560 | tmp = ata_wait_register(port_mmio + PORT_CMD, | 588 | if (rc == -EOPNOTSUPP) { |
561 | PORT_CMD_CLO, PORT_CMD_CLO, 1, 500); | 589 | reason = "port busy but CLO unavailable"; |
562 | if (tmp & PORT_CMD_CLO) { | 590 | goto fail_restart; |
563 | rc = -EIO; | 591 | } else if (rc) { |
564 | reason = "CLO failed"; | 592 | reason = "port busy but CLO failed"; |
565 | goto fail_restart; | 593 | goto fail_restart; |
566 | } | 594 | } |
567 | } | 595 | } |
@@ -672,6 +700,12 @@ static void ahci_postreset(struct ata_port *ap, unsigned int *class) | |||
672 | 700 | ||
673 | static int ahci_probe_reset(struct ata_port *ap, unsigned int *classes) | 701 | static int ahci_probe_reset(struct ata_port *ap, unsigned int *classes) |
674 | { | 702 | { |
703 | if ((ap->flags & AHCI_FLAG_RESET_NEEDS_CLO) && | ||
704 | (ata_busy_wait(ap, ATA_BUSY, 1000) & ATA_BUSY)) { | ||
705 | /* ATA_BUSY hasn't cleared, so send a CLO */ | ||
706 | ahci_clo(ap); | ||
707 | } | ||
708 | |||
675 | return ata_drive_probe_reset(ap, ata_std_probeinit, | 709 | return ata_drive_probe_reset(ap, ata_std_probeinit, |
676 | ahci_softreset, ahci_hardreset, | 710 | ahci_softreset, ahci_hardreset, |
677 | ahci_postreset, classes); | 711 | ahci_postreset, classes); |