diff options
Diffstat (limited to 'drivers')
| -rw-r--r-- | drivers/ata/ahci.c | 99 |
1 files changed, 82 insertions, 17 deletions
diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index 1c62b8e39645..966ab401e523 100644 --- a/drivers/ata/ahci.c +++ b/drivers/ata/ahci.c | |||
| @@ -255,6 +255,8 @@ static void ahci_pmp_attach(struct ata_port *ap); | |||
| 255 | static void ahci_pmp_detach(struct ata_port *ap); | 255 | static void ahci_pmp_detach(struct ata_port *ap); |
| 256 | static int ahci_softreset(struct ata_link *link, unsigned int *class, | 256 | static int ahci_softreset(struct ata_link *link, unsigned int *class, |
| 257 | unsigned long deadline); | 257 | unsigned long deadline); |
| 258 | static int ahci_sb600_softreset(struct ata_link *link, unsigned int *class, | ||
| 259 | unsigned long deadline); | ||
| 258 | static int ahci_hardreset(struct ata_link *link, unsigned int *class, | 260 | static int ahci_hardreset(struct ata_link *link, unsigned int *class, |
| 259 | unsigned long deadline); | 261 | unsigned long deadline); |
| 260 | 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, |
| @@ -331,6 +333,12 @@ static struct ata_port_operations ahci_p5wdh_ops = { | |||
| 331 | .hardreset = ahci_p5wdh_hardreset, | 333 | .hardreset = ahci_p5wdh_hardreset, |
| 332 | }; | 334 | }; |
| 333 | 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 | |||
| 334 | #define AHCI_HFLAGS(flags) .private_data = (void *)(flags) | 342 | #define AHCI_HFLAGS(flags) .private_data = (void *)(flags) |
| 335 | 343 | ||
| 336 | static const struct ata_port_info ahci_port_info[] = { | 344 | static const struct ata_port_info ahci_port_info[] = { |
| @@ -361,11 +369,11 @@ static const struct ata_port_info ahci_port_info[] = { | |||
| 361 | { | 369 | { |
| 362 | AHCI_HFLAGS (AHCI_HFLAG_IGN_SERR_INTERNAL | | 370 | AHCI_HFLAGS (AHCI_HFLAG_IGN_SERR_INTERNAL | |
| 363 | AHCI_HFLAG_32BIT_ONLY | AHCI_HFLAG_NO_MSI | | 371 | AHCI_HFLAG_32BIT_ONLY | AHCI_HFLAG_NO_MSI | |
| 364 | AHCI_HFLAG_SECT255 | AHCI_HFLAG_NO_PMP), | 372 | AHCI_HFLAG_SECT255), |
| 365 | .flags = AHCI_FLAG_COMMON, | 373 | .flags = AHCI_FLAG_COMMON, |
| 366 | .pio_mask = 0x1f, /* pio0-4 */ | 374 | .pio_mask = 0x1f, /* pio0-4 */ |
| 367 | .udma_mask = ATA_UDMA6, | 375 | .udma_mask = ATA_UDMA6, |
| 368 | .port_ops = &ahci_ops, | 376 | .port_ops = &ahci_sb600_ops, |
| 369 | }, | 377 | }, |
| 370 | /* board_ahci_mv */ | 378 | /* board_ahci_mv */ |
| 371 | { | 379 | { |
| @@ -379,12 +387,11 @@ static const struct ata_port_info ahci_port_info[] = { | |||
| 379 | }, | 387 | }, |
| 380 | /* board_ahci_sb700 */ | 388 | /* board_ahci_sb700 */ |
| 381 | { | 389 | { |
| 382 | AHCI_HFLAGS (AHCI_HFLAG_IGN_SERR_INTERNAL | | 390 | AHCI_HFLAGS (AHCI_HFLAG_IGN_SERR_INTERNAL), |
| 383 | AHCI_HFLAG_NO_PMP), | ||
| 384 | .flags = AHCI_FLAG_COMMON, | 391 | .flags = AHCI_FLAG_COMMON, |
| 385 | .pio_mask = 0x1f, /* pio0-4 */ | 392 | .pio_mask = 0x1f, /* pio0-4 */ |
| 386 | .udma_mask = ATA_UDMA6, | 393 | .udma_mask = ATA_UDMA6, |
| 387 | .port_ops = &ahci_ops, | 394 | .port_ops = &ahci_sb600_ops, |
| 388 | }, | 395 | }, |
| 389 | /* board_ahci_mcp65 */ | 396 | /* board_ahci_mcp65 */ |
| 390 | { | 397 | { |
| @@ -1278,19 +1285,11 @@ static int ahci_exec_polled_cmd(struct ata_port *ap, int pmp, | |||
| 1278 | return 0; | 1285 | return 0; |
| 1279 | } | 1286 | } |
| 1280 | 1287 | ||
| 1281 | static int ahci_check_ready(struct ata_link *link) | 1288 | static int ahci_do_softreset(struct ata_link *link, unsigned int *class, |
| 1282 | { | 1289 | int pmp, unsigned long deadline, |
| 1283 | void __iomem *port_mmio = ahci_port_base(link->ap); | 1290 | int (*check_ready)(struct ata_link *link)) |
| 1284 | u8 status = readl(port_mmio + PORT_TFDATA) & 0xFF; | ||
| 1285 | |||
| 1286 | return ata_check_ready(status); | ||
| 1287 | } | ||
| 1288 | |||
| 1289 | static int ahci_softreset(struct ata_link *link, unsigned int *class, | ||
| 1290 | unsigned long deadline) | ||
| 1291 | { | 1291 | { |
| 1292 | struct ata_port *ap = link->ap; | 1292 | struct ata_port *ap = link->ap; |
| 1293 | int pmp = sata_srst_pmp(link); | ||
| 1294 | const char *reason = NULL; | 1293 | const char *reason = NULL; |
| 1295 | unsigned long now, msecs; | 1294 | unsigned long now, msecs; |
| 1296 | struct ata_taskfile tf; | 1295 | struct ata_taskfile tf; |
| @@ -1328,7 +1327,7 @@ static int ahci_softreset(struct ata_link *link, unsigned int *class, | |||
| 1328 | ahci_exec_polled_cmd(ap, pmp, &tf, 0, 0, 0); | 1327 | ahci_exec_polled_cmd(ap, pmp, &tf, 0, 0, 0); |
| 1329 | 1328 | ||
| 1330 | /* wait for link to become ready */ | 1329 | /* wait for link to become ready */ |
| 1331 | rc = ata_wait_after_reset(link, deadline, ahci_check_ready); | 1330 | rc = ata_wait_after_reset(link, deadline, check_ready); |
| 1332 | /* link occupied, -ENODEV too is an error */ | 1331 | /* link occupied, -ENODEV too is an error */ |
| 1333 | if (rc) { | 1332 | if (rc) { |
| 1334 | reason = "device not ready"; | 1333 | reason = "device not ready"; |
| @@ -1344,6 +1343,72 @@ static int ahci_softreset(struct ata_link *link, unsigned int *class, | |||
| 1344 | return rc; | 1343 | return rc; |
| 1345 | } | 1344 | } |
| 1346 | 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 | |||
| 1347 | static int ahci_hardreset(struct ata_link *link, unsigned int *class, | 1412 | static int ahci_hardreset(struct ata_link *link, unsigned int *class, |
| 1348 | unsigned long deadline) | 1413 | unsigned long deadline) |
| 1349 | { | 1414 | { |
