diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2008-06-13 10:40:57 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2008-06-13 10:40:57 -0400 |
commit | fa8d84b78be10d0c455dd716f40152bb7003bd5b (patch) | |
tree | 8b051596111afec96c2fb687a57414340e76c4d5 | |
parent | 51558576ead54c1047e4d41440626e4f9aa015ea (diff) | |
parent | bd17243a84632465f5403bc9eb8b4831bd67e582 (diff) |
Merge branch 'upstream-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jgarzik/libata-dev
* 'upstream-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jgarzik/libata-dev:
ahci: Workaround HW bug for SB600/700 SATA controller PMP support
ahci: workarounds for mcp65
-rw-r--r-- | drivers/ata/ahci.c | 137 |
1 files changed, 112 insertions, 25 deletions
diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index 544b7d6c617c..966ab401e523 100644 --- a/drivers/ata/ahci.c +++ b/drivers/ata/ahci.c | |||
@@ -89,6 +89,7 @@ enum { | |||
89 | board_ahci_sb600 = 3, | 89 | board_ahci_sb600 = 3, |
90 | board_ahci_mv = 4, | 90 | board_ahci_mv = 4, |
91 | board_ahci_sb700 = 5, | 91 | board_ahci_sb700 = 5, |
92 | board_ahci_mcp65 = 6, | ||
92 | 93 | ||
93 | /* global controller registers */ | 94 | /* global controller registers */ |
94 | HOST_CAP = 0x00, /* host capabilities */ | 95 | HOST_CAP = 0x00, /* host capabilities */ |
@@ -190,6 +191,7 @@ enum { | |||
190 | AHCI_HFLAG_NO_PMP = (1 << 6), /* no PMP */ | 191 | AHCI_HFLAG_NO_PMP = (1 << 6), /* no PMP */ |
191 | AHCI_HFLAG_NO_HOTPLUG = (1 << 7), /* ignore PxSERR.DIAG.N */ | 192 | AHCI_HFLAG_NO_HOTPLUG = (1 << 7), /* ignore PxSERR.DIAG.N */ |
192 | AHCI_HFLAG_SECT255 = (1 << 8), /* max 255 sectors */ | 193 | AHCI_HFLAG_SECT255 = (1 << 8), /* max 255 sectors */ |
194 | AHCI_HFLAG_YES_NCQ = (1 << 9), /* force NCQ cap on */ | ||
193 | 195 | ||
194 | /* ap->flags bits */ | 196 | /* ap->flags bits */ |
195 | 197 | ||
@@ -253,6 +255,8 @@ static void ahci_pmp_attach(struct ata_port *ap); | |||
253 | static void ahci_pmp_detach(struct ata_port *ap); | 255 | static void ahci_pmp_detach(struct ata_port *ap); |
254 | static int ahci_softreset(struct ata_link *link, unsigned int *class, | 256 | static int ahci_softreset(struct ata_link *link, unsigned int *class, |
255 | unsigned long deadline); | 257 | unsigned long deadline); |
258 | static int ahci_sb600_softreset(struct ata_link *link, unsigned int *class, | ||
259 | unsigned long deadline); | ||
256 | static int ahci_hardreset(struct ata_link *link, unsigned int *class, | 260 | static int ahci_hardreset(struct ata_link *link, unsigned int *class, |
257 | unsigned long deadline); | 261 | unsigned long deadline); |
258 | static int ahci_vt8251_hardreset(struct ata_link *link, unsigned int *class, | 262 | static int ahci_vt8251_hardreset(struct ata_link *link, unsigned int *class, |
@@ -329,6 +333,12 @@ static struct ata_port_operations ahci_p5wdh_ops = { | |||
329 | .hardreset = ahci_p5wdh_hardreset, | 333 | .hardreset = ahci_p5wdh_hardreset, |
330 | }; | 334 | }; |
331 | 335 | ||
336 | static struct ata_port_operations ahci_sb600_ops = { | ||
337 | .inherits = &ahci_ops, | ||
338 | .softreset = ahci_sb600_softreset, | ||
339 | .pmp_softreset = ahci_sb600_softreset, | ||
340 | }; | ||
341 | |||
332 | #define AHCI_HFLAGS(flags) .private_data = (void *)(flags) | 342 | #define AHCI_HFLAGS(flags) .private_data = (void *)(flags) |
333 | 343 | ||
334 | static const struct ata_port_info ahci_port_info[] = { | 344 | static const struct ata_port_info ahci_port_info[] = { |
@@ -359,11 +369,11 @@ static const struct ata_port_info ahci_port_info[] = { | |||
359 | { | 369 | { |
360 | AHCI_HFLAGS (AHCI_HFLAG_IGN_SERR_INTERNAL | | 370 | AHCI_HFLAGS (AHCI_HFLAG_IGN_SERR_INTERNAL | |
361 | AHCI_HFLAG_32BIT_ONLY | AHCI_HFLAG_NO_MSI | | 371 | AHCI_HFLAG_32BIT_ONLY | AHCI_HFLAG_NO_MSI | |
362 | AHCI_HFLAG_SECT255 | AHCI_HFLAG_NO_PMP), | 372 | AHCI_HFLAG_SECT255), |
363 | .flags = AHCI_FLAG_COMMON, | 373 | .flags = AHCI_FLAG_COMMON, |
364 | .pio_mask = 0x1f, /* pio0-4 */ | 374 | .pio_mask = 0x1f, /* pio0-4 */ |
365 | .udma_mask = ATA_UDMA6, | 375 | .udma_mask = ATA_UDMA6, |
366 | .port_ops = &ahci_ops, | 376 | .port_ops = &ahci_sb600_ops, |
367 | }, | 377 | }, |
368 | /* board_ahci_mv */ | 378 | /* board_ahci_mv */ |
369 | { | 379 | { |
@@ -377,8 +387,15 @@ static const struct ata_port_info ahci_port_info[] = { | |||
377 | }, | 387 | }, |
378 | /* board_ahci_sb700 */ | 388 | /* board_ahci_sb700 */ |
379 | { | 389 | { |
380 | AHCI_HFLAGS (AHCI_HFLAG_IGN_SERR_INTERNAL | | 390 | AHCI_HFLAGS (AHCI_HFLAG_IGN_SERR_INTERNAL), |
381 | AHCI_HFLAG_NO_PMP), | 391 | .flags = AHCI_FLAG_COMMON, |
392 | .pio_mask = 0x1f, /* pio0-4 */ | ||
393 | .udma_mask = ATA_UDMA6, | ||
394 | .port_ops = &ahci_sb600_ops, | ||
395 | }, | ||
396 | /* board_ahci_mcp65 */ | ||
397 | { | ||
398 | AHCI_HFLAGS (AHCI_HFLAG_YES_NCQ), | ||
382 | .flags = AHCI_FLAG_COMMON, | 399 | .flags = AHCI_FLAG_COMMON, |
383 | .pio_mask = 0x1f, /* pio0-4 */ | 400 | .pio_mask = 0x1f, /* pio0-4 */ |
384 | .udma_mask = ATA_UDMA6, | 401 | .udma_mask = ATA_UDMA6, |
@@ -438,14 +455,14 @@ static const struct pci_device_id ahci_pci_tbl[] = { | |||
438 | { PCI_VDEVICE(VIA, 0x6287), board_ahci_vt8251 }, /* VIA VT8251 */ | 455 | { PCI_VDEVICE(VIA, 0x6287), board_ahci_vt8251 }, /* VIA VT8251 */ |
439 | 456 | ||
440 | /* NVIDIA */ | 457 | /* NVIDIA */ |
441 | { PCI_VDEVICE(NVIDIA, 0x044c), board_ahci }, /* MCP65 */ | 458 | { PCI_VDEVICE(NVIDIA, 0x044c), board_ahci_mcp65 }, /* MCP65 */ |
442 | { PCI_VDEVICE(NVIDIA, 0x044d), board_ahci }, /* MCP65 */ | 459 | { PCI_VDEVICE(NVIDIA, 0x044d), board_ahci_mcp65 }, /* MCP65 */ |
443 | { PCI_VDEVICE(NVIDIA, 0x044e), board_ahci }, /* MCP65 */ | 460 | { PCI_VDEVICE(NVIDIA, 0x044e), board_ahci_mcp65 }, /* MCP65 */ |
444 | { PCI_VDEVICE(NVIDIA, 0x044f), board_ahci }, /* MCP65 */ | 461 | { PCI_VDEVICE(NVIDIA, 0x044f), board_ahci_mcp65 }, /* MCP65 */ |
445 | { PCI_VDEVICE(NVIDIA, 0x045c), board_ahci }, /* MCP65 */ | 462 | { PCI_VDEVICE(NVIDIA, 0x045c), board_ahci_mcp65 }, /* MCP65 */ |
446 | { PCI_VDEVICE(NVIDIA, 0x045d), board_ahci }, /* MCP65 */ | 463 | { PCI_VDEVICE(NVIDIA, 0x045d), board_ahci_mcp65 }, /* MCP65 */ |
447 | { PCI_VDEVICE(NVIDIA, 0x045e), board_ahci }, /* MCP65 */ | 464 | { PCI_VDEVICE(NVIDIA, 0x045e), board_ahci_mcp65 }, /* MCP65 */ |
448 | { PCI_VDEVICE(NVIDIA, 0x045f), board_ahci }, /* MCP65 */ | 465 | { PCI_VDEVICE(NVIDIA, 0x045f), board_ahci_mcp65 }, /* MCP65 */ |
449 | { PCI_VDEVICE(NVIDIA, 0x0550), board_ahci }, /* MCP67 */ | 466 | { PCI_VDEVICE(NVIDIA, 0x0550), board_ahci }, /* MCP67 */ |
450 | { PCI_VDEVICE(NVIDIA, 0x0551), board_ahci }, /* MCP67 */ | 467 | { PCI_VDEVICE(NVIDIA, 0x0551), board_ahci }, /* MCP67 */ |
451 | { PCI_VDEVICE(NVIDIA, 0x0552), board_ahci }, /* MCP67 */ | 468 | { PCI_VDEVICE(NVIDIA, 0x0552), board_ahci }, /* MCP67 */ |
@@ -624,6 +641,12 @@ static void ahci_save_initial_config(struct pci_dev *pdev, | |||
624 | cap &= ~HOST_CAP_NCQ; | 641 | cap &= ~HOST_CAP_NCQ; |
625 | } | 642 | } |
626 | 643 | ||
644 | if (!(cap & HOST_CAP_NCQ) && (hpriv->flags & AHCI_HFLAG_YES_NCQ)) { | ||
645 | dev_printk(KERN_INFO, &pdev->dev, | ||
646 | "controller can do NCQ, turning on CAP_NCQ\n"); | ||
647 | cap |= HOST_CAP_NCQ; | ||
648 | } | ||
649 | |||
627 | if ((cap & HOST_CAP_PMP) && (hpriv->flags & AHCI_HFLAG_NO_PMP)) { | 650 | if ((cap & HOST_CAP_PMP) && (hpriv->flags & AHCI_HFLAG_NO_PMP)) { |
628 | dev_printk(KERN_INFO, &pdev->dev, | 651 | dev_printk(KERN_INFO, &pdev->dev, |
629 | "controller can't do PMP, turning off CAP_PMP\n"); | 652 | "controller can't do PMP, turning off CAP_PMP\n"); |
@@ -1262,19 +1285,11 @@ static int ahci_exec_polled_cmd(struct ata_port *ap, int pmp, | |||
1262 | return 0; | 1285 | return 0; |
1263 | } | 1286 | } |
1264 | 1287 | ||
1265 | static int ahci_check_ready(struct ata_link *link) | 1288 | static int ahci_do_softreset(struct ata_link *link, unsigned int *class, |
1266 | { | 1289 | int pmp, unsigned long deadline, |
1267 | void __iomem *port_mmio = ahci_port_base(link->ap); | 1290 | int (*check_ready)(struct ata_link *link)) |
1268 | u8 status = readl(port_mmio + PORT_TFDATA) & 0xFF; | ||
1269 | |||
1270 | return ata_check_ready(status); | ||
1271 | } | ||
1272 | |||
1273 | static int ahci_softreset(struct ata_link *link, unsigned int *class, | ||
1274 | unsigned long deadline) | ||
1275 | { | 1291 | { |
1276 | struct ata_port *ap = link->ap; | 1292 | struct ata_port *ap = link->ap; |
1277 | int pmp = sata_srst_pmp(link); | ||
1278 | const char *reason = NULL; | 1293 | const char *reason = NULL; |
1279 | unsigned long now, msecs; | 1294 | unsigned long now, msecs; |
1280 | struct ata_taskfile tf; | 1295 | struct ata_taskfile tf; |
@@ -1312,7 +1327,7 @@ static int ahci_softreset(struct ata_link *link, unsigned int *class, | |||
1312 | ahci_exec_polled_cmd(ap, pmp, &tf, 0, 0, 0); | 1327 | ahci_exec_polled_cmd(ap, pmp, &tf, 0, 0, 0); |
1313 | 1328 | ||
1314 | /* wait for link to become ready */ | 1329 | /* wait for link to become ready */ |
1315 | rc = ata_wait_after_reset(link, deadline, ahci_check_ready); | 1330 | rc = ata_wait_after_reset(link, deadline, check_ready); |
1316 | /* link occupied, -ENODEV too is an error */ | 1331 | /* link occupied, -ENODEV too is an error */ |
1317 | if (rc) { | 1332 | if (rc) { |
1318 | reason = "device not ready"; | 1333 | reason = "device not ready"; |
@@ -1328,6 +1343,72 @@ static int ahci_softreset(struct ata_link *link, unsigned int *class, | |||
1328 | return rc; | 1343 | return rc; |
1329 | } | 1344 | } |
1330 | 1345 | ||
1346 | static int ahci_check_ready(struct ata_link *link) | ||
1347 | { | ||
1348 | void __iomem *port_mmio = ahci_port_base(link->ap); | ||
1349 | u8 status = readl(port_mmio + PORT_TFDATA) & 0xFF; | ||
1350 | |||
1351 | return ata_check_ready(status); | ||
1352 | } | ||
1353 | |||
1354 | static int ahci_softreset(struct ata_link *link, unsigned int *class, | ||
1355 | unsigned long deadline) | ||
1356 | { | ||
1357 | int pmp = sata_srst_pmp(link); | ||
1358 | |||
1359 | DPRINTK("ENTER\n"); | ||
1360 | |||
1361 | return ahci_do_softreset(link, class, pmp, deadline, ahci_check_ready); | ||
1362 | } | ||
1363 | |||
1364 | static int ahci_sb600_check_ready(struct ata_link *link) | ||
1365 | { | ||
1366 | void __iomem *port_mmio = ahci_port_base(link->ap); | ||
1367 | u8 status = readl(port_mmio + PORT_TFDATA) & 0xFF; | ||
1368 | u32 irq_status = readl(port_mmio + PORT_IRQ_STAT); | ||
1369 | |||
1370 | /* | ||
1371 | * There is no need to check TFDATA if BAD PMP is found due to HW bug, | ||
1372 | * which can save timeout delay. | ||
1373 | */ | ||
1374 | if (irq_status & PORT_IRQ_BAD_PMP) | ||
1375 | return -EIO; | ||
1376 | |||
1377 | return ata_check_ready(status); | ||
1378 | } | ||
1379 | |||
1380 | static int ahci_sb600_softreset(struct ata_link *link, unsigned int *class, | ||
1381 | unsigned long deadline) | ||
1382 | { | ||
1383 | struct ata_port *ap = link->ap; | ||
1384 | void __iomem *port_mmio = ahci_port_base(ap); | ||
1385 | int pmp = sata_srst_pmp(link); | ||
1386 | int rc; | ||
1387 | u32 irq_sts; | ||
1388 | |||
1389 | DPRINTK("ENTER\n"); | ||
1390 | |||
1391 | rc = ahci_do_softreset(link, class, pmp, deadline, | ||
1392 | ahci_sb600_check_ready); | ||
1393 | |||
1394 | /* | ||
1395 | * Soft reset fails on some ATI chips with IPMS set when PMP | ||
1396 | * is enabled but SATA HDD/ODD is connected to SATA port, | ||
1397 | * do soft reset again to port 0. | ||
1398 | */ | ||
1399 | if (rc == -EIO) { | ||
1400 | irq_sts = readl(port_mmio + PORT_IRQ_STAT); | ||
1401 | if (irq_sts & PORT_IRQ_BAD_PMP) { | ||
1402 | ata_link_printk(link, KERN_WARNING, | ||
1403 | "failed due to HW bug, retry pmp=0\n"); | ||
1404 | rc = ahci_do_softreset(link, class, 0, deadline, | ||
1405 | ahci_check_ready); | ||
1406 | } | ||
1407 | } | ||
1408 | |||
1409 | return rc; | ||
1410 | } | ||
1411 | |||
1331 | static int ahci_hardreset(struct ata_link *link, unsigned int *class, | 1412 | static int ahci_hardreset(struct ata_link *link, unsigned int *class, |
1332 | unsigned long deadline) | 1413 | unsigned long deadline) |
1333 | { | 1414 | { |
@@ -2118,7 +2199,8 @@ static void ahci_p5wdh_workaround(struct ata_host *host) | |||
2118 | static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) | 2199 | static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) |
2119 | { | 2200 | { |
2120 | static int printed_version; | 2201 | static int printed_version; |
2121 | struct ata_port_info pi = ahci_port_info[ent->driver_data]; | 2202 | unsigned int board_id = ent->driver_data; |
2203 | struct ata_port_info pi = ahci_port_info[board_id]; | ||
2122 | const struct ata_port_info *ppi[] = { &pi, NULL }; | 2204 | const struct ata_port_info *ppi[] = { &pi, NULL }; |
2123 | struct device *dev = &pdev->dev; | 2205 | struct device *dev = &pdev->dev; |
2124 | struct ahci_host_priv *hpriv; | 2206 | struct ahci_host_priv *hpriv; |
@@ -2167,6 +2249,11 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) | |||
2167 | return -ENOMEM; | 2249 | return -ENOMEM; |
2168 | hpriv->flags |= (unsigned long)pi.private_data; | 2250 | hpriv->flags |= (unsigned long)pi.private_data; |
2169 | 2251 | ||
2252 | /* MCP65 revision A1 and A2 can't do MSI */ | ||
2253 | if (board_id == board_ahci_mcp65 && | ||
2254 | (pdev->revision == 0xa1 || pdev->revision == 0xa2)) | ||
2255 | hpriv->flags |= AHCI_HFLAG_NO_MSI; | ||
2256 | |||
2170 | if ((hpriv->flags & AHCI_HFLAG_NO_MSI) || pci_enable_msi(pdev)) | 2257 | if ((hpriv->flags & AHCI_HFLAG_NO_MSI) || pci_enable_msi(pdev)) |
2171 | pci_intx(pdev, 1); | 2258 | pci_intx(pdev, 1); |
2172 | 2259 | ||