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 /drivers | |
| 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
Diffstat (limited to 'drivers')
| -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 | ||
