Skip to content

Commit

Permalink
ahci: Workaround HW bug for SB600/700 SATA controller PMP support
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Shane Huang authored and Jeff Garzik committed Jun 13, 2008
1 parent e297d99 commit bd17243
Showing 1 changed file with 82 additions and 17 deletions.
99 changes: 82 additions & 17 deletions drivers/ata/ahci.c
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,8 @@ static void ahci_pmp_attach(struct ata_port *ap);
static void ahci_pmp_detach(struct ata_port *ap);
static int ahci_softreset(struct ata_link *link, unsigned int *class,
unsigned long deadline);
static int ahci_sb600_softreset(struct ata_link *link, unsigned int *class,
unsigned long deadline);
static int ahci_hardreset(struct ata_link *link, unsigned int *class,
unsigned long deadline);
static int ahci_vt8251_hardreset(struct ata_link *link, unsigned int *class,
Expand Down Expand Up @@ -331,6 +333,12 @@ static struct ata_port_operations ahci_p5wdh_ops = {
.hardreset = ahci_p5wdh_hardreset,
};

static struct ata_port_operations ahci_sb600_ops = {
.inherits = &ahci_ops,
.softreset = ahci_sb600_softreset,
.pmp_softreset = ahci_sb600_softreset,
};

#define AHCI_HFLAGS(flags) .private_data = (void *)(flags)

static const struct ata_port_info ahci_port_info[] = {
Expand Down Expand Up @@ -361,11 +369,11 @@ static const struct ata_port_info ahci_port_info[] = {
{
AHCI_HFLAGS (AHCI_HFLAG_IGN_SERR_INTERNAL |
AHCI_HFLAG_32BIT_ONLY | AHCI_HFLAG_NO_MSI |
AHCI_HFLAG_SECT255 | AHCI_HFLAG_NO_PMP),
AHCI_HFLAG_SECT255),
.flags = AHCI_FLAG_COMMON,
.pio_mask = 0x1f, /* pio0-4 */
.udma_mask = ATA_UDMA6,
.port_ops = &ahci_ops,
.port_ops = &ahci_sb600_ops,
},
/* board_ahci_mv */
{
Expand All @@ -379,12 +387,11 @@ static const struct ata_port_info ahci_port_info[] = {
},
/* board_ahci_sb700 */
{
AHCI_HFLAGS (AHCI_HFLAG_IGN_SERR_INTERNAL |
AHCI_HFLAG_NO_PMP),
AHCI_HFLAGS (AHCI_HFLAG_IGN_SERR_INTERNAL),
.flags = AHCI_FLAG_COMMON,
.pio_mask = 0x1f, /* pio0-4 */
.udma_mask = ATA_UDMA6,
.port_ops = &ahci_ops,
.port_ops = &ahci_sb600_ops,
},
/* board_ahci_mcp65 */
{
Expand Down Expand Up @@ -1278,19 +1285,11 @@ static int ahci_exec_polled_cmd(struct ata_port *ap, int pmp,
return 0;
}

static int ahci_check_ready(struct ata_link *link)
{
void __iomem *port_mmio = ahci_port_base(link->ap);
u8 status = readl(port_mmio + PORT_TFDATA) & 0xFF;

return ata_check_ready(status);
}

static int ahci_softreset(struct ata_link *link, unsigned int *class,
unsigned long deadline)
static int ahci_do_softreset(struct ata_link *link, unsigned int *class,
int pmp, unsigned long deadline,
int (*check_ready)(struct ata_link *link))
{
struct ata_port *ap = link->ap;
int pmp = sata_srst_pmp(link);
const char *reason = NULL;
unsigned long now, msecs;
struct ata_taskfile tf;
Expand Down Expand Up @@ -1328,7 +1327,7 @@ static int ahci_softreset(struct ata_link *link, unsigned int *class,
ahci_exec_polled_cmd(ap, pmp, &tf, 0, 0, 0);

/* wait for link to become ready */
rc = ata_wait_after_reset(link, deadline, ahci_check_ready);
rc = ata_wait_after_reset(link, deadline, check_ready);
/* link occupied, -ENODEV too is an error */
if (rc) {
reason = "device not ready";
Expand All @@ -1344,6 +1343,72 @@ static int ahci_softreset(struct ata_link *link, unsigned int *class,
return rc;
}

static int ahci_check_ready(struct ata_link *link)
{
void __iomem *port_mmio = ahci_port_base(link->ap);
u8 status = readl(port_mmio + PORT_TFDATA) & 0xFF;

return ata_check_ready(status);
}

static int ahci_softreset(struct ata_link *link, unsigned int *class,
unsigned long deadline)
{
int pmp = sata_srst_pmp(link);

DPRINTK("ENTER\n");

return ahci_do_softreset(link, class, pmp, deadline, ahci_check_ready);
}

static int ahci_sb600_check_ready(struct ata_link *link)
{
void __iomem *port_mmio = ahci_port_base(link->ap);
u8 status = readl(port_mmio + PORT_TFDATA) & 0xFF;
u32 irq_status = readl(port_mmio + PORT_IRQ_STAT);

/*
* There is no need to check TFDATA if BAD PMP is found due to HW bug,
* which can save timeout delay.
*/
if (irq_status & PORT_IRQ_BAD_PMP)
return -EIO;

return ata_check_ready(status);
}

static int ahci_sb600_softreset(struct ata_link *link, unsigned int *class,
unsigned long deadline)
{
struct ata_port *ap = link->ap;
void __iomem *port_mmio = ahci_port_base(ap);
int pmp = sata_srst_pmp(link);
int rc;
u32 irq_sts;

DPRINTK("ENTER\n");

rc = ahci_do_softreset(link, class, pmp, deadline,
ahci_sb600_check_ready);

/*
* Soft reset fails on some ATI chips with IPMS set when PMP
* is enabled but SATA HDD/ODD is connected to SATA port,
* do soft reset again to port 0.
*/
if (rc == -EIO) {
irq_sts = readl(port_mmio + PORT_IRQ_STAT);
if (irq_sts & PORT_IRQ_BAD_PMP) {
ata_link_printk(link, KERN_WARNING,
"failed due to HW bug, retry pmp=0\n");
rc = ahci_do_softreset(link, class, 0, deadline,
ahci_check_ready);
}
}

return rc;
}

static int ahci_hardreset(struct ata_link *link, unsigned int *class,
unsigned long deadline)
{
Expand Down

0 comments on commit bd17243

Please sign in to comment.