aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorShane Huang <shane.huang@amd.com>2008-06-10 03:52:04 -0400
committerJeff Garzik <jgarzik@redhat.com>2008-06-13 02:46:55 -0400
commitbd17243a84632465f5403bc9eb8b4831bd67e582 (patch)
treea98d7a5590c57a45659de395b8a510fc5df7ffeb
parente297d99e103f951a71fcb1534f1ff3480dd3a851 (diff)
ahci: Workaround HW bug for SB600/700 SATA controller PMP support
There is one bug in ATI SATA PMP of SB600 and SB700 old revision, which leads to soft reset failure. This patch can fix the bug. Signed-off-by: Shane Huang <shane.huang@amd.com> Acked-by: Tejun Heo <htejun@gmail.com> Signed-off-by: Jeff Garzik <jgarzik@redhat.com>
-rw-r--r--drivers/ata/ahci.c99
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);
255static void ahci_pmp_detach(struct ata_port *ap); 255static void ahci_pmp_detach(struct ata_port *ap);
256static int ahci_softreset(struct ata_link *link, unsigned int *class, 256static int ahci_softreset(struct ata_link *link, unsigned int *class,
257 unsigned long deadline); 257 unsigned long deadline);
258static int ahci_sb600_softreset(struct ata_link *link, unsigned int *class,
259 unsigned long deadline);
258static int ahci_hardreset(struct ata_link *link, unsigned int *class, 260static int ahci_hardreset(struct ata_link *link, unsigned int *class,
259 unsigned long deadline); 261 unsigned long deadline);
260static int ahci_vt8251_hardreset(struct ata_link *link, unsigned int *class, 262static 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
336static 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
336static const struct ata_port_info ahci_port_info[] = { 344static 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
1281static int ahci_check_ready(struct ata_link *link) 1288static 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
1289static 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
1346static 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
1354static 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
1364static 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
1380static 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
1347static int ahci_hardreset(struct ata_link *link, unsigned int *class, 1412static int ahci_hardreset(struct ata_link *link, unsigned int *class,
1348 unsigned long deadline) 1413 unsigned long deadline)
1349{ 1414{