mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-12-14 22:44:27 +08:00
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 */
|
||||
dma_addr_t cmd_block_dma; /* DMA base addr for them */
|
||||
struct ata_taskfile tf; /* Cached taskfile registers */
|
||||
int do_port_rst;
|
||||
};
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
void __iomem *port = ap->ioaddr.cmd_addr;
|
||||
struct sil24_port_priv *pp = ap->private_data;
|
||||
u32 tmp;
|
||||
|
||||
/* 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,
|
||||
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 0;
|
||||
}
|
||||
|
||||
@ -692,10 +723,34 @@ static int sil24_hardreset(struct ata_link *link, unsigned int *class,
|
||||
{
|
||||
struct ata_port *ap = link->ap;
|
||||
void __iomem *port = ap->ioaddr.cmd_addr;
|
||||
struct sil24_port_priv *pp = ap->private_data;
|
||||
int did_port_rst = 0;
|
||||
const char *reason;
|
||||
int tout_msec, rc;
|
||||
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 */
|
||||
sata_set_spd(link);
|
||||
|
||||
@ -732,6 +787,11 @@ static int sil24_hardreset(struct ata_link *link, unsigned int *class,
|
||||
return -EAGAIN;
|
||||
|
||||
err:
|
||||
if (!did_port_rst) {
|
||||
pp->do_port_rst = 1;
|
||||
goto retry;
|
||||
}
|
||||
|
||||
ata_link_printk(link, KERN_ERR, "hardreset failed (%s)\n", reason);
|
||||
return -EIO;
|
||||
}
|
||||
@ -997,6 +1057,7 @@ static void sil24_error_intr(struct ata_port *ap)
|
||||
ehi->err_mask |= AC_ERR_OTHER;
|
||||
ehi->action |= ATA_EH_HARDRESET;
|
||||
ata_ehi_push_desc(ehi, "PMP DMA CS errata");
|
||||
pp->do_port_rst = 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)
|
||||
{
|
||||
struct sil24_port_priv *pp = ap->private_data;
|
||||
|
||||
if (sil24_init_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,
|
||||
sil24_pmp_softreset, sil24_pmp_hardreset,
|
||||
sata_pmp_std_postreset);
|
||||
|
||||
pp->do_port_rst = 0;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
void __iomem *host_base = host->iomap[SIL24_HOST_BAR];
|
||||
void __iomem *port_base = host->iomap[SIL24_PORT_BAR];
|
||||
u32 tmp;
|
||||
int i;
|
||||
|
||||
@ -1218,7 +1282,8 @@ static void sil24_init_controller(struct ata_host *host)
|
||||
|
||||
/* init ports */
|
||||
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 */
|
||||
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");
|
||||
}
|
||||
|
||||
/* Configure IRQ WoC */
|
||||
if (host->ports[0]->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);
|
||||
/* configure port */
|
||||
sil24_config_port(ap);
|
||||
}
|
||||
|
||||
/* Turn on interrupts */
|
||||
|
Loading…
Reference in New Issue
Block a user