diff options
Diffstat (limited to 'drivers/ata/ahci.c')
-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 | { |