sata_sil24: implement PORT_RST
As DEV_RST (hardreset) sometimes fail to recover the controller (especially after PMP DMA CS errata). In such cases, perform PORT_RST prior to DEV_RST. Signed-off-by: Tejun Heo <htejun@gmail.com> Signed-off-by: Jeff Garzik <jeff@garzik.org>
This commit is contained in:
parent
3454dc6922
commit
238180343e
|
@ -323,6 +323,7 @@ struct sil24_port_priv {
|
||||||
union sil24_cmd_block *cmd_block; /* 32 cmd blocks */
|
union sil24_cmd_block *cmd_block; /* 32 cmd blocks */
|
||||||
dma_addr_t cmd_block_dma; /* DMA base addr for them */
|
dma_addr_t cmd_block_dma; /* DMA base addr for them */
|
||||||
struct ata_taskfile tf; /* Cached taskfile registers */
|
struct ata_taskfile tf; /* Cached taskfile registers */
|
||||||
|
int do_port_rst;
|
||||||
};
|
};
|
||||||
|
|
||||||
static void sil24_dev_config(struct ata_device *dev);
|
static void sil24_dev_config(struct ata_device *dev);
|
||||||
|
@ -536,6 +537,31 @@ static void sil24_tf_read(struct ata_port *ap, struct ata_taskfile *tf)
|
||||||
*tf = pp->tf;
|
*tf = pp->tf;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void sil24_config_port(struct ata_port *ap)
|
||||||
|
{
|
||||||
|
void __iomem *port = ap->ioaddr.cmd_addr;
|
||||||
|
|
||||||
|
/* configure IRQ WoC */
|
||||||
|
if (ap->flags & SIL24_FLAG_PCIX_IRQ_WOC)
|
||||||
|
writel(PORT_CS_IRQ_WOC, port + PORT_CTRL_STAT);
|
||||||
|
else
|
||||||
|
writel(PORT_CS_IRQ_WOC, port + PORT_CTRL_CLR);
|
||||||
|
|
||||||
|
/* zero error counters. */
|
||||||
|
writel(0x8000, port + PORT_DECODE_ERR_THRESH);
|
||||||
|
writel(0x8000, port + PORT_CRC_ERR_THRESH);
|
||||||
|
writel(0x8000, port + PORT_HSHK_ERR_THRESH);
|
||||||
|
writel(0x0000, port + PORT_DECODE_ERR_CNT);
|
||||||
|
writel(0x0000, port + PORT_CRC_ERR_CNT);
|
||||||
|
writel(0x0000, port + PORT_HSHK_ERR_CNT);
|
||||||
|
|
||||||
|
/* always use 64bit activation */
|
||||||
|
writel(PORT_CS_32BIT_ACTV, port + PORT_CTRL_CLR);
|
||||||
|
|
||||||
|
/* clear port multiplier enable and resume bits */
|
||||||
|
writel(PORT_CS_PMP_EN | PORT_CS_PMP_RESUME, port + PORT_CTRL_CLR);
|
||||||
|
}
|
||||||
|
|
||||||
static void sil24_config_pmp(struct ata_port *ap, int attached)
|
static void sil24_config_pmp(struct ata_port *ap, int attached)
|
||||||
{
|
{
|
||||||
void __iomem *port = ap->ioaddr.cmd_addr;
|
void __iomem *port = ap->ioaddr.cmd_addr;
|
||||||
|
@ -564,6 +590,7 @@ static void sil24_clear_pmp(struct ata_port *ap)
|
||||||
static int sil24_init_port(struct ata_port *ap)
|
static int sil24_init_port(struct ata_port *ap)
|
||||||
{
|
{
|
||||||
void __iomem *port = ap->ioaddr.cmd_addr;
|
void __iomem *port = ap->ioaddr.cmd_addr;
|
||||||
|
struct sil24_port_priv *pp = ap->private_data;
|
||||||
u32 tmp;
|
u32 tmp;
|
||||||
|
|
||||||
/* clear PMP error status */
|
/* clear PMP error status */
|
||||||
|
@ -576,8 +603,12 @@ static int sil24_init_port(struct ata_port *ap)
|
||||||
tmp = ata_wait_register(port + PORT_CTRL_STAT,
|
tmp = ata_wait_register(port + PORT_CTRL_STAT,
|
||||||
PORT_CS_RDY, 0, 10, 100);
|
PORT_CS_RDY, 0, 10, 100);
|
||||||
|
|
||||||
if ((tmp & (PORT_CS_INIT | PORT_CS_RDY)) != PORT_CS_RDY)
|
if ((tmp & (PORT_CS_INIT | PORT_CS_RDY)) != PORT_CS_RDY) {
|
||||||
|
pp->do_port_rst = 1;
|
||||||
|
ap->link.eh_context.i.action |= ATA_EH_HARDRESET;
|
||||||
return -EIO;
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -692,10 +723,34 @@ static int sil24_hardreset(struct ata_link *link, unsigned int *class,
|
||||||
{
|
{
|
||||||
struct ata_port *ap = link->ap;
|
struct ata_port *ap = link->ap;
|
||||||
void __iomem *port = ap->ioaddr.cmd_addr;
|
void __iomem *port = ap->ioaddr.cmd_addr;
|
||||||
|
struct sil24_port_priv *pp = ap->private_data;
|
||||||
|
int did_port_rst = 0;
|
||||||
const char *reason;
|
const char *reason;
|
||||||
int tout_msec, rc;
|
int tout_msec, rc;
|
||||||
u32 tmp;
|
u32 tmp;
|
||||||
|
|
||||||
|
retry:
|
||||||
|
/* Sometimes, DEV_RST is not enough to recover the controller.
|
||||||
|
* This happens often after PM DMA CS errata.
|
||||||
|
*/
|
||||||
|
if (pp->do_port_rst) {
|
||||||
|
ata_port_printk(ap, KERN_WARNING, "controller in dubious "
|
||||||
|
"state, performing PORT_RST\n");
|
||||||
|
|
||||||
|
writel(PORT_CS_PORT_RST, port + PORT_CTRL_STAT);
|
||||||
|
msleep(10);
|
||||||
|
writel(PORT_CS_PORT_RST, port + PORT_CTRL_CLR);
|
||||||
|
ata_wait_register(port + PORT_CTRL_STAT, PORT_CS_RDY, 0,
|
||||||
|
10, 5000);
|
||||||
|
|
||||||
|
/* restore port configuration */
|
||||||
|
sil24_config_port(ap);
|
||||||
|
sil24_config_pmp(ap, ap->nr_pmp_links);
|
||||||
|
|
||||||
|
pp->do_port_rst = 0;
|
||||||
|
did_port_rst = 1;
|
||||||
|
}
|
||||||
|
|
||||||
/* sil24 does the right thing(tm) without any protection */
|
/* sil24 does the right thing(tm) without any protection */
|
||||||
sata_set_spd(link);
|
sata_set_spd(link);
|
||||||
|
|
||||||
|
@ -732,6 +787,11 @@ static int sil24_hardreset(struct ata_link *link, unsigned int *class,
|
||||||
return -EAGAIN;
|
return -EAGAIN;
|
||||||
|
|
||||||
err:
|
err:
|
||||||
|
if (!did_port_rst) {
|
||||||
|
pp->do_port_rst = 1;
|
||||||
|
goto retry;
|
||||||
|
}
|
||||||
|
|
||||||
ata_link_printk(link, KERN_ERR, "hardreset failed (%s)\n", reason);
|
ata_link_printk(link, KERN_ERR, "hardreset failed (%s)\n", reason);
|
||||||
return -EIO;
|
return -EIO;
|
||||||
}
|
}
|
||||||
|
@ -997,6 +1057,7 @@ static void sil24_error_intr(struct ata_port *ap)
|
||||||
ehi->err_mask |= AC_ERR_OTHER;
|
ehi->err_mask |= AC_ERR_OTHER;
|
||||||
ehi->action |= ATA_EH_HARDRESET;
|
ehi->action |= ATA_EH_HARDRESET;
|
||||||
ata_ehi_push_desc(ehi, "PMP DMA CS errata");
|
ata_ehi_push_desc(ehi, "PMP DMA CS errata");
|
||||||
|
pp->do_port_rst = 1;
|
||||||
freeze = 1;
|
freeze = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1152,6 +1213,8 @@ static irqreturn_t sil24_interrupt(int irq, void *dev_instance)
|
||||||
|
|
||||||
static void sil24_error_handler(struct ata_port *ap)
|
static void sil24_error_handler(struct ata_port *ap)
|
||||||
{
|
{
|
||||||
|
struct sil24_port_priv *pp = ap->private_data;
|
||||||
|
|
||||||
if (sil24_init_port(ap))
|
if (sil24_init_port(ap))
|
||||||
ata_eh_freeze_port(ap);
|
ata_eh_freeze_port(ap);
|
||||||
|
|
||||||
|
@ -1160,6 +1223,8 @@ static void sil24_error_handler(struct ata_port *ap)
|
||||||
ata_std_postreset, sata_pmp_std_prereset,
|
ata_std_postreset, sata_pmp_std_prereset,
|
||||||
sil24_pmp_softreset, sil24_pmp_hardreset,
|
sil24_pmp_softreset, sil24_pmp_hardreset,
|
||||||
sata_pmp_std_postreset);
|
sata_pmp_std_postreset);
|
||||||
|
|
||||||
|
pp->do_port_rst = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void sil24_post_internal_cmd(struct ata_queued_cmd *qc)
|
static void sil24_post_internal_cmd(struct ata_queued_cmd *qc)
|
||||||
|
@ -1206,7 +1271,6 @@ static int sil24_port_start(struct ata_port *ap)
|
||||||
static void sil24_init_controller(struct ata_host *host)
|
static void sil24_init_controller(struct ata_host *host)
|
||||||
{
|
{
|
||||||
void __iomem *host_base = host->iomap[SIL24_HOST_BAR];
|
void __iomem *host_base = host->iomap[SIL24_HOST_BAR];
|
||||||
void __iomem *port_base = host->iomap[SIL24_PORT_BAR];
|
|
||||||
u32 tmp;
|
u32 tmp;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
@ -1218,7 +1282,8 @@ static void sil24_init_controller(struct ata_host *host)
|
||||||
|
|
||||||
/* init ports */
|
/* init ports */
|
||||||
for (i = 0; i < host->n_ports; i++) {
|
for (i = 0; i < host->n_ports; i++) {
|
||||||
void __iomem *port = port_base + i * PORT_REGS_SIZE;
|
struct ata_port *ap = host->ports[i];
|
||||||
|
void __iomem *port = ap->ioaddr.cmd_addr;
|
||||||
|
|
||||||
/* Initial PHY setting */
|
/* Initial PHY setting */
|
||||||
writel(0x20c, port + PORT_PHY_CFG);
|
writel(0x20c, port + PORT_PHY_CFG);
|
||||||
|
@ -1235,26 +1300,8 @@ static void sil24_init_controller(struct ata_host *host)
|
||||||
"failed to clear port RST\n");
|
"failed to clear port RST\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Configure IRQ WoC */
|
/* configure port */
|
||||||
if (host->ports[0]->flags & SIL24_FLAG_PCIX_IRQ_WOC)
|
sil24_config_port(ap);
|
||||||
writel(PORT_CS_IRQ_WOC, port + PORT_CTRL_STAT);
|
|
||||||
else
|
|
||||||
writel(PORT_CS_IRQ_WOC, port + PORT_CTRL_CLR);
|
|
||||||
|
|
||||||
/* Zero error counters. */
|
|
||||||
writel(0x8000, port + PORT_DECODE_ERR_THRESH);
|
|
||||||
writel(0x8000, port + PORT_CRC_ERR_THRESH);
|
|
||||||
writel(0x8000, port + PORT_HSHK_ERR_THRESH);
|
|
||||||
writel(0x0000, port + PORT_DECODE_ERR_CNT);
|
|
||||||
writel(0x0000, port + PORT_CRC_ERR_CNT);
|
|
||||||
writel(0x0000, port + PORT_HSHK_ERR_CNT);
|
|
||||||
|
|
||||||
/* Always use 64bit activation */
|
|
||||||
writel(PORT_CS_32BIT_ACTV, port + PORT_CTRL_CLR);
|
|
||||||
|
|
||||||
/* Clear port multiplier enable and resume bits */
|
|
||||||
writel(PORT_CS_PMP_EN | PORT_CS_PMP_RESUME,
|
|
||||||
port + PORT_CTRL_CLR);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Turn on interrupts */
|
/* Turn on interrupts */
|
||||||
|
|
Loading…
Reference in New Issue