diff options
Diffstat (limited to 'drivers/ata')
-rw-r--r-- | drivers/ata/ahci.c | 144 |
1 files changed, 144 insertions, 0 deletions
diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index 95229e77bffe..49cf4cf1a5a2 100644 --- a/drivers/ata/ahci.c +++ b/drivers/ata/ahci.c | |||
@@ -41,6 +41,7 @@ | |||
41 | #include <linux/interrupt.h> | 41 | #include <linux/interrupt.h> |
42 | #include <linux/dma-mapping.h> | 42 | #include <linux/dma-mapping.h> |
43 | #include <linux/device.h> | 43 | #include <linux/device.h> |
44 | #include <linux/dmi.h> | ||
44 | #include <scsi/scsi_host.h> | 45 | #include <scsi/scsi_host.h> |
45 | #include <scsi/scsi_cmnd.h> | 46 | #include <scsi/scsi_cmnd.h> |
46 | #include <linux/libata.h> | 47 | #include <linux/libata.h> |
@@ -241,6 +242,7 @@ static void ahci_pmp_attach(struct ata_port *ap); | |||
241 | static void ahci_pmp_detach(struct ata_port *ap); | 242 | static void ahci_pmp_detach(struct ata_port *ap); |
242 | static void ahci_error_handler(struct ata_port *ap); | 243 | static void ahci_error_handler(struct ata_port *ap); |
243 | static void ahci_vt8251_error_handler(struct ata_port *ap); | 244 | static void ahci_vt8251_error_handler(struct ata_port *ap); |
245 | static void ahci_p5wdh_error_handler(struct ata_port *ap); | ||
244 | static void ahci_post_internal_cmd(struct ata_queued_cmd *qc); | 246 | static void ahci_post_internal_cmd(struct ata_queued_cmd *qc); |
245 | static int ahci_port_resume(struct ata_port *ap); | 247 | static int ahci_port_resume(struct ata_port *ap); |
246 | static unsigned int ahci_fill_sg(struct ata_queued_cmd *qc, void *cmd_tbl); | 248 | static unsigned int ahci_fill_sg(struct ata_queued_cmd *qc, void *cmd_tbl); |
@@ -339,6 +341,40 @@ static const struct ata_port_operations ahci_vt8251_ops = { | |||
339 | .port_stop = ahci_port_stop, | 341 | .port_stop = ahci_port_stop, |
340 | }; | 342 | }; |
341 | 343 | ||
344 | static const struct ata_port_operations ahci_p5wdh_ops = { | ||
345 | .check_status = ahci_check_status, | ||
346 | .check_altstatus = ahci_check_status, | ||
347 | .dev_select = ata_noop_dev_select, | ||
348 | |||
349 | .tf_read = ahci_tf_read, | ||
350 | |||
351 | .qc_defer = sata_pmp_qc_defer_cmd_switch, | ||
352 | .qc_prep = ahci_qc_prep, | ||
353 | .qc_issue = ahci_qc_issue, | ||
354 | |||
355 | .irq_clear = ahci_irq_clear, | ||
356 | |||
357 | .scr_read = ahci_scr_read, | ||
358 | .scr_write = ahci_scr_write, | ||
359 | |||
360 | .freeze = ahci_freeze, | ||
361 | .thaw = ahci_thaw, | ||
362 | |||
363 | .error_handler = ahci_p5wdh_error_handler, | ||
364 | .post_internal_cmd = ahci_post_internal_cmd, | ||
365 | |||
366 | .pmp_attach = ahci_pmp_attach, | ||
367 | .pmp_detach = ahci_pmp_detach, | ||
368 | |||
369 | #ifdef CONFIG_PM | ||
370 | .port_suspend = ahci_port_suspend, | ||
371 | .port_resume = ahci_port_resume, | ||
372 | #endif | ||
373 | |||
374 | .port_start = ahci_port_start, | ||
375 | .port_stop = ahci_port_stop, | ||
376 | }; | ||
377 | |||
342 | #define AHCI_HFLAGS(flags) .private_data = (void *)(flags) | 378 | #define AHCI_HFLAGS(flags) .private_data = (void *)(flags) |
343 | 379 | ||
344 | static const struct ata_port_info ahci_port_info[] = { | 380 | static const struct ata_port_info ahci_port_info[] = { |
@@ -1213,6 +1249,53 @@ static int ahci_vt8251_hardreset(struct ata_link *link, unsigned int *class, | |||
1213 | return rc ?: -EAGAIN; | 1249 | return rc ?: -EAGAIN; |
1214 | } | 1250 | } |
1215 | 1251 | ||
1252 | static int ahci_p5wdh_hardreset(struct ata_link *link, unsigned int *class, | ||
1253 | unsigned long deadline) | ||
1254 | { | ||
1255 | struct ata_port *ap = link->ap; | ||
1256 | struct ahci_port_priv *pp = ap->private_data; | ||
1257 | u8 *d2h_fis = pp->rx_fis + RX_FIS_D2H_REG; | ||
1258 | struct ata_taskfile tf; | ||
1259 | int rc; | ||
1260 | |||
1261 | ahci_stop_engine(ap); | ||
1262 | |||
1263 | /* clear D2H reception area to properly wait for D2H FIS */ | ||
1264 | ata_tf_init(link->device, &tf); | ||
1265 | tf.command = 0x80; | ||
1266 | ata_tf_to_fis(&tf, 0, 0, d2h_fis); | ||
1267 | |||
1268 | rc = sata_link_hardreset(link, sata_ehc_deb_timing(&link->eh_context), | ||
1269 | deadline); | ||
1270 | |||
1271 | ahci_start_engine(ap); | ||
1272 | |||
1273 | if (rc || ata_link_offline(link)) | ||
1274 | return rc; | ||
1275 | |||
1276 | /* spec mandates ">= 2ms" before checking status */ | ||
1277 | msleep(150); | ||
1278 | |||
1279 | /* The pseudo configuration device on SIMG4726 attached to | ||
1280 | * ASUS P5W-DH Deluxe doesn't send signature FIS after | ||
1281 | * hardreset if no device is attached to the first downstream | ||
1282 | * port && the pseudo device locks up on SRST w/ PMP==0. To | ||
1283 | * work around this, wait for !BSY only briefly. If BSY isn't | ||
1284 | * cleared, perform CLO and proceed to IDENTIFY (achieved by | ||
1285 | * ATA_LFLAG_NO_SRST and ATA_LFLAG_ASSUME_ATA). | ||
1286 | * | ||
1287 | * Wait for two seconds. Devices attached to downstream port | ||
1288 | * which can't process the following IDENTIFY after this will | ||
1289 | * have to be reset again. For most cases, this should | ||
1290 | * suffice while making probing snappish enough. | ||
1291 | */ | ||
1292 | rc = ata_wait_ready(ap, jiffies + 2 * HZ); | ||
1293 | if (rc) | ||
1294 | ahci_kick_engine(ap, 0); | ||
1295 | |||
1296 | return 0; | ||
1297 | } | ||
1298 | |||
1216 | static void ahci_postreset(struct ata_link *link, unsigned int *class) | 1299 | static void ahci_postreset(struct ata_link *link, unsigned int *class) |
1217 | { | 1300 | { |
1218 | struct ata_port *ap = link->ap; | 1301 | struct ata_port *ap = link->ap; |
@@ -1670,6 +1753,19 @@ static void ahci_vt8251_error_handler(struct ata_port *ap) | |||
1670 | ahci_postreset); | 1753 | ahci_postreset); |
1671 | } | 1754 | } |
1672 | 1755 | ||
1756 | static void ahci_p5wdh_error_handler(struct ata_port *ap) | ||
1757 | { | ||
1758 | if (!(ap->pflags & ATA_PFLAG_FROZEN)) { | ||
1759 | /* restart engine */ | ||
1760 | ahci_stop_engine(ap); | ||
1761 | ahci_start_engine(ap); | ||
1762 | } | ||
1763 | |||
1764 | /* perform recovery */ | ||
1765 | ata_do_eh(ap, ata_std_prereset, ahci_softreset, ahci_p5wdh_hardreset, | ||
1766 | ahci_postreset); | ||
1767 | } | ||
1768 | |||
1673 | static void ahci_post_internal_cmd(struct ata_queued_cmd *qc) | 1769 | static void ahci_post_internal_cmd(struct ata_queued_cmd *qc) |
1674 | { | 1770 | { |
1675 | struct ata_port *ap = qc->ap; | 1771 | struct ata_port *ap = qc->ap; |
@@ -1955,6 +2051,51 @@ static void ahci_print_info(struct ata_host *host) | |||
1955 | ); | 2051 | ); |
1956 | } | 2052 | } |
1957 | 2053 | ||
2054 | /* On ASUS P5W DH Deluxe, the second port of PCI device 00:1f.2 is | ||
2055 | * hardwired to on-board SIMG 4726. The chipset is ICH8 and doesn't | ||
2056 | * support PMP and the 4726 either directly exports the device | ||
2057 | * attached to the first downstream port or acts as a hardware storage | ||
2058 | * controller and emulate a single ATA device (can be RAID 0/1 or some | ||
2059 | * other configuration). | ||
2060 | * | ||
2061 | * When there's no device attached to the first downstream port of the | ||
2062 | * 4726, "Config Disk" appears, which is a pseudo ATA device to | ||
2063 | * configure the 4726. However, ATA emulation of the device is very | ||
2064 | * lame. It doesn't send signature D2H Reg FIS after the initial | ||
2065 | * hardreset, pukes on SRST w/ PMP==0 and has bunch of other issues. | ||
2066 | * | ||
2067 | * The following function works around the problem by always using | ||
2068 | * hardreset on the port and not depending on receiving signature FIS | ||
2069 | * afterward. If signature FIS isn't received soon, ATA class is | ||
2070 | * assumed without follow-up softreset. | ||
2071 | */ | ||
2072 | static void ahci_p5wdh_workaround(struct ata_host *host) | ||
2073 | { | ||
2074 | static struct dmi_system_id sysids[] = { | ||
2075 | { | ||
2076 | .ident = "P5W DH Deluxe", | ||
2077 | .matches = { | ||
2078 | DMI_MATCH(DMI_SYS_VENDOR, | ||
2079 | "ASUSTEK COMPUTER INC"), | ||
2080 | DMI_MATCH(DMI_PRODUCT_NAME, "P5W DH Deluxe"), | ||
2081 | }, | ||
2082 | }, | ||
2083 | { } | ||
2084 | }; | ||
2085 | struct pci_dev *pdev = to_pci_dev(host->dev); | ||
2086 | |||
2087 | if (pdev->bus->number == 0 && pdev->devfn == PCI_DEVFN(0x1f, 2) && | ||
2088 | dmi_check_system(sysids)) { | ||
2089 | struct ata_port *ap = host->ports[1]; | ||
2090 | |||
2091 | dev_printk(KERN_INFO, &pdev->dev, "enabling ASUS P5W DH " | ||
2092 | "Deluxe on-board SIMG4726 workaround\n"); | ||
2093 | |||
2094 | ap->ops = &ahci_p5wdh_ops; | ||
2095 | ap->link.flags |= ATA_LFLAG_NO_SRST | ATA_LFLAG_ASSUME_ATA; | ||
2096 | } | ||
2097 | } | ||
2098 | |||
1958 | static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) | 2099 | static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) |
1959 | { | 2100 | { |
1960 | static int printed_version; | 2101 | static int printed_version; |
@@ -2024,6 +2165,9 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) | |||
2024 | ap->ops = &ata_dummy_port_ops; | 2165 | ap->ops = &ata_dummy_port_ops; |
2025 | } | 2166 | } |
2026 | 2167 | ||
2168 | /* apply workaround for ASUS P5W DH Deluxe mainboard */ | ||
2169 | ahci_p5wdh_workaround(host); | ||
2170 | |||
2027 | /* initialize adapter */ | 2171 | /* initialize adapter */ |
2028 | rc = ahci_configure_dma_masks(pdev, hpriv->cap & HOST_CAP_64); | 2172 | rc = ahci_configure_dma_masks(pdev, hpriv->cap & HOST_CAP_64); |
2029 | if (rc) | 2173 | if (rc) |