[PATCH] sata_sil24: convert to new EH
Convert sata_sil24 to new EH. * When port is frozen, IRQ for the port is masked. * sil24_softreset() doesn't need to mangle with IRQ mask anymore. libata ensures that the port is frozen during reset. * Only turn on interrupts which are handled by interrupt handler and EH. As we don't handle SDB notify yet, turn it off. DEV_XCHG and UNK_FIS are handled by EH and thus turned on. * sil24_softreset() usually fails to recover the port after DEV_XCHG. ATA_PORT_HARDRESET is used as recovery action for DEV_XCHG. * sil24 may be invoked without any active command. e.g. DEV_XCHG irq occuring while no qc in progress still triggers EH and will reset the port and revalidate attached device. Signed-off-by: Tejun Heo <htejun@gmail.com>
This commit is contained in:
parent
2a3917a8bb
commit
88ce7550c3
|
@ -156,6 +156,9 @@ enum {
|
||||||
PORT_IRQ_HANDSHAKE = (1 << 10), /* handshake error threshold */
|
PORT_IRQ_HANDSHAKE = (1 << 10), /* handshake error threshold */
|
||||||
PORT_IRQ_SDB_NOTIFY = (1 << 11), /* SDB notify received */
|
PORT_IRQ_SDB_NOTIFY = (1 << 11), /* SDB notify received */
|
||||||
|
|
||||||
|
DEF_PORT_IRQ = PORT_IRQ_COMPLETE | PORT_IRQ_ERROR |
|
||||||
|
PORT_IRQ_DEV_XCHG | PORT_IRQ_UNK_FIS,
|
||||||
|
|
||||||
/* bits[27:16] are unmasked (raw) */
|
/* bits[27:16] are unmasked (raw) */
|
||||||
PORT_IRQ_RAW_SHIFT = 16,
|
PORT_IRQ_RAW_SHIFT = 16,
|
||||||
PORT_IRQ_MASKED_MASK = 0x7ff,
|
PORT_IRQ_MASKED_MASK = 0x7ff,
|
||||||
|
@ -242,6 +245,58 @@ union sil24_cmd_block {
|
||||||
struct sil24_atapi_block atapi;
|
struct sil24_atapi_block atapi;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static struct sil24_cerr_info {
|
||||||
|
unsigned int err_mask, action;
|
||||||
|
const char *desc;
|
||||||
|
} sil24_cerr_db[] = {
|
||||||
|
[0] = { AC_ERR_DEV, ATA_EH_REVALIDATE,
|
||||||
|
"device error" },
|
||||||
|
[PORT_CERR_DEV] = { AC_ERR_DEV, ATA_EH_REVALIDATE,
|
||||||
|
"device error via D2H FIS" },
|
||||||
|
[PORT_CERR_SDB] = { AC_ERR_DEV, ATA_EH_REVALIDATE,
|
||||||
|
"device error via SDB FIS" },
|
||||||
|
[PORT_CERR_DATA] = { AC_ERR_ATA_BUS, ATA_EH_SOFTRESET,
|
||||||
|
"error in data FIS" },
|
||||||
|
[PORT_CERR_SEND] = { AC_ERR_ATA_BUS, ATA_EH_SOFTRESET,
|
||||||
|
"failed to transmit command FIS" },
|
||||||
|
[PORT_CERR_INCONSISTENT] = { AC_ERR_HSM, ATA_EH_SOFTRESET,
|
||||||
|
"protocol mismatch" },
|
||||||
|
[PORT_CERR_DIRECTION] = { AC_ERR_HSM, ATA_EH_SOFTRESET,
|
||||||
|
"data directon mismatch" },
|
||||||
|
[PORT_CERR_UNDERRUN] = { AC_ERR_HSM, ATA_EH_SOFTRESET,
|
||||||
|
"ran out of SGEs while writing" },
|
||||||
|
[PORT_CERR_OVERRUN] = { AC_ERR_HSM, ATA_EH_SOFTRESET,
|
||||||
|
"ran out of SGEs while reading" },
|
||||||
|
[PORT_CERR_PKT_PROT] = { AC_ERR_HSM, ATA_EH_SOFTRESET,
|
||||||
|
"invalid data directon for ATAPI CDB" },
|
||||||
|
[PORT_CERR_SGT_BOUNDARY] = { AC_ERR_SYSTEM, ATA_EH_SOFTRESET,
|
||||||
|
"SGT no on qword boundary" },
|
||||||
|
[PORT_CERR_SGT_TGTABRT] = { AC_ERR_HOST_BUS, ATA_EH_SOFTRESET,
|
||||||
|
"PCI target abort while fetching SGT" },
|
||||||
|
[PORT_CERR_SGT_MSTABRT] = { AC_ERR_HOST_BUS, ATA_EH_SOFTRESET,
|
||||||
|
"PCI master abort while fetching SGT" },
|
||||||
|
[PORT_CERR_SGT_PCIPERR] = { AC_ERR_HOST_BUS, ATA_EH_SOFTRESET,
|
||||||
|
"PCI parity error while fetching SGT" },
|
||||||
|
[PORT_CERR_CMD_BOUNDARY] = { AC_ERR_SYSTEM, ATA_EH_SOFTRESET,
|
||||||
|
"PRB not on qword boundary" },
|
||||||
|
[PORT_CERR_CMD_TGTABRT] = { AC_ERR_HOST_BUS, ATA_EH_SOFTRESET,
|
||||||
|
"PCI target abort while fetching PRB" },
|
||||||
|
[PORT_CERR_CMD_MSTABRT] = { AC_ERR_HOST_BUS, ATA_EH_SOFTRESET,
|
||||||
|
"PCI master abort while fetching PRB" },
|
||||||
|
[PORT_CERR_CMD_PCIPERR] = { AC_ERR_HOST_BUS, ATA_EH_SOFTRESET,
|
||||||
|
"PCI parity error while fetching PRB" },
|
||||||
|
[PORT_CERR_XFR_UNDEF] = { AC_ERR_HOST_BUS, ATA_EH_SOFTRESET,
|
||||||
|
"undefined error while transferring data" },
|
||||||
|
[PORT_CERR_XFR_TGTABRT] = { AC_ERR_HOST_BUS, ATA_EH_SOFTRESET,
|
||||||
|
"PCI target abort while transferring data" },
|
||||||
|
[PORT_CERR_XFR_MSTABRT] = { AC_ERR_HOST_BUS, ATA_EH_SOFTRESET,
|
||||||
|
"PCI master abort while transferring data" },
|
||||||
|
[PORT_CERR_XFR_PCIPERR] = { AC_ERR_HOST_BUS, ATA_EH_SOFTRESET,
|
||||||
|
"PCI parity error while transferring data" },
|
||||||
|
[PORT_CERR_SENDSERVICE] = { AC_ERR_HSM, ATA_EH_SOFTRESET,
|
||||||
|
"FIS received while sending service FIS" },
|
||||||
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ap->private_data
|
* ap->private_data
|
||||||
*
|
*
|
||||||
|
@ -269,8 +324,11 @@ static int sil24_probe_reset(struct ata_port *ap, unsigned int *classes);
|
||||||
static void sil24_qc_prep(struct ata_queued_cmd *qc);
|
static void sil24_qc_prep(struct ata_queued_cmd *qc);
|
||||||
static unsigned int sil24_qc_issue(struct ata_queued_cmd *qc);
|
static unsigned int sil24_qc_issue(struct ata_queued_cmd *qc);
|
||||||
static void sil24_irq_clear(struct ata_port *ap);
|
static void sil24_irq_clear(struct ata_port *ap);
|
||||||
static void sil24_eng_timeout(struct ata_port *ap);
|
|
||||||
static irqreturn_t sil24_interrupt(int irq, void *dev_instance, struct pt_regs *regs);
|
static irqreturn_t sil24_interrupt(int irq, void *dev_instance, struct pt_regs *regs);
|
||||||
|
static void sil24_freeze(struct ata_port *ap);
|
||||||
|
static void sil24_thaw(struct ata_port *ap);
|
||||||
|
static void sil24_error_handler(struct ata_port *ap);
|
||||||
|
static void sil24_post_internal_cmd(struct ata_queued_cmd *qc);
|
||||||
static int sil24_port_start(struct ata_port *ap);
|
static int sil24_port_start(struct ata_port *ap);
|
||||||
static void sil24_port_stop(struct ata_port *ap);
|
static void sil24_port_stop(struct ata_port *ap);
|
||||||
static void sil24_host_stop(struct ata_host_set *host_set);
|
static void sil24_host_stop(struct ata_host_set *host_set);
|
||||||
|
@ -325,14 +383,17 @@ static const struct ata_port_operations sil24_ops = {
|
||||||
.qc_prep = sil24_qc_prep,
|
.qc_prep = sil24_qc_prep,
|
||||||
.qc_issue = sil24_qc_issue,
|
.qc_issue = sil24_qc_issue,
|
||||||
|
|
||||||
.eng_timeout = sil24_eng_timeout,
|
|
||||||
|
|
||||||
.irq_handler = sil24_interrupt,
|
.irq_handler = sil24_interrupt,
|
||||||
.irq_clear = sil24_irq_clear,
|
.irq_clear = sil24_irq_clear,
|
||||||
|
|
||||||
.scr_read = sil24_scr_read,
|
.scr_read = sil24_scr_read,
|
||||||
.scr_write = sil24_scr_write,
|
.scr_write = sil24_scr_write,
|
||||||
|
|
||||||
|
.freeze = sil24_freeze,
|
||||||
|
.thaw = sil24_thaw,
|
||||||
|
.error_handler = sil24_error_handler,
|
||||||
|
.post_internal_cmd = sil24_post_internal_cmd,
|
||||||
|
|
||||||
.port_start = sil24_port_start,
|
.port_start = sil24_port_start,
|
||||||
.port_stop = sil24_port_stop,
|
.port_stop = sil24_port_stop,
|
||||||
.host_stop = sil24_host_stop,
|
.host_stop = sil24_host_stop,
|
||||||
|
@ -459,7 +520,7 @@ static int sil24_softreset(struct ata_port *ap, unsigned int *class)
|
||||||
struct sil24_port_priv *pp = ap->private_data;
|
struct sil24_port_priv *pp = ap->private_data;
|
||||||
struct sil24_prb *prb = &pp->cmd_block[0].ata.prb;
|
struct sil24_prb *prb = &pp->cmd_block[0].ata.prb;
|
||||||
dma_addr_t paddr = pp->cmd_block_dma;
|
dma_addr_t paddr = pp->cmd_block_dma;
|
||||||
u32 mask, irq_enable, irq_stat;
|
u32 mask, irq_stat;
|
||||||
const char *reason;
|
const char *reason;
|
||||||
|
|
||||||
DPRINTK("ENTER\n");
|
DPRINTK("ENTER\n");
|
||||||
|
@ -470,10 +531,6 @@ static int sil24_softreset(struct ata_port *ap, unsigned int *class)
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* temporarily turn off IRQs during SRST */
|
|
||||||
irq_enable = readl(port + PORT_IRQ_ENABLE_SET);
|
|
||||||
writel(irq_enable, port + PORT_IRQ_ENABLE_CLR);
|
|
||||||
|
|
||||||
/* put the port into known state */
|
/* put the port into known state */
|
||||||
if (sil24_init_port(ap)) {
|
if (sil24_init_port(ap)) {
|
||||||
reason ="port not ready";
|
reason ="port not ready";
|
||||||
|
@ -494,9 +551,6 @@ static int sil24_softreset(struct ata_port *ap, unsigned int *class)
|
||||||
writel(irq_stat, port + PORT_IRQ_STAT); /* clear IRQs */
|
writel(irq_stat, port + PORT_IRQ_STAT); /* clear IRQs */
|
||||||
irq_stat >>= PORT_IRQ_RAW_SHIFT;
|
irq_stat >>= PORT_IRQ_RAW_SHIFT;
|
||||||
|
|
||||||
/* restore IRQs */
|
|
||||||
writel(irq_enable, port + PORT_IRQ_ENABLE_SET);
|
|
||||||
|
|
||||||
if (!(irq_stat & PORT_IRQ_COMPLETE)) {
|
if (!(irq_stat & PORT_IRQ_COMPLETE)) {
|
||||||
if (irq_stat & PORT_IRQ_ERROR)
|
if (irq_stat & PORT_IRQ_ERROR)
|
||||||
reason = "SRST command error";
|
reason = "SRST command error";
|
||||||
|
@ -655,158 +709,134 @@ static void sil24_irq_clear(struct ata_port *ap)
|
||||||
/* unused */
|
/* unused */
|
||||||
}
|
}
|
||||||
|
|
||||||
static int __sil24_restart_controller(void __iomem *port)
|
static void sil24_freeze(struct ata_port *ap)
|
||||||
{
|
{
|
||||||
u32 tmp;
|
|
||||||
int cnt;
|
|
||||||
|
|
||||||
writel(PORT_CS_INIT, port + PORT_CTRL_STAT);
|
|
||||||
|
|
||||||
/* Max ~10ms */
|
|
||||||
for (cnt = 0; cnt < 10000; cnt++) {
|
|
||||||
tmp = readl(port + PORT_CTRL_STAT);
|
|
||||||
if (tmp & PORT_CS_RDY)
|
|
||||||
return 0;
|
|
||||||
udelay(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void sil24_restart_controller(struct ata_port *ap)
|
|
||||||
{
|
|
||||||
if (__sil24_restart_controller((void __iomem *)ap->ioaddr.cmd_addr))
|
|
||||||
printk(KERN_ERR DRV_NAME
|
|
||||||
" ata%u: failed to restart controller\n", ap->id);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int __sil24_reset_controller(void __iomem *port)
|
|
||||||
{
|
|
||||||
int cnt;
|
|
||||||
u32 tmp;
|
|
||||||
|
|
||||||
/* Reset controller state. Is this correct? */
|
|
||||||
writel(PORT_CS_DEV_RST, port + PORT_CTRL_STAT);
|
|
||||||
readl(port + PORT_CTRL_STAT); /* sync */
|
|
||||||
|
|
||||||
/* Max ~100ms */
|
|
||||||
for (cnt = 0; cnt < 1000; cnt++) {
|
|
||||||
udelay(100);
|
|
||||||
tmp = readl(port + PORT_CTRL_STAT);
|
|
||||||
if (!(tmp & PORT_CS_DEV_RST))
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tmp & PORT_CS_DEV_RST)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
if (tmp & PORT_CS_RDY)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
return __sil24_restart_controller(port);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void sil24_reset_controller(struct ata_port *ap)
|
|
||||||
{
|
|
||||||
printk(KERN_NOTICE DRV_NAME
|
|
||||||
" ata%u: resetting controller...\n", ap->id);
|
|
||||||
if (__sil24_reset_controller((void __iomem *)ap->ioaddr.cmd_addr))
|
|
||||||
printk(KERN_ERR DRV_NAME
|
|
||||||
" ata%u: failed to reset controller\n", ap->id);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void sil24_eng_timeout(struct ata_port *ap)
|
|
||||||
{
|
|
||||||
struct ata_queued_cmd *qc;
|
|
||||||
|
|
||||||
qc = ata_qc_from_tag(ap, ap->active_tag);
|
|
||||||
|
|
||||||
ata_port_printk(ap, KERN_ERR, "command timeout\n");
|
|
||||||
qc->err_mask |= AC_ERR_TIMEOUT;
|
|
||||||
ata_eh_qc_complete(qc);
|
|
||||||
|
|
||||||
sil24_reset_controller(ap);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void sil24_error_intr(struct ata_port *ap, u32 slot_stat)
|
|
||||||
{
|
|
||||||
struct ata_queued_cmd *qc = ata_qc_from_tag(ap, ap->active_tag);
|
|
||||||
struct sil24_port_priv *pp = ap->private_data;
|
|
||||||
void __iomem *port = (void __iomem *)ap->ioaddr.cmd_addr;
|
void __iomem *port = (void __iomem *)ap->ioaddr.cmd_addr;
|
||||||
u32 irq_stat, cmd_err, sstatus, serror;
|
|
||||||
unsigned int err_mask;
|
|
||||||
|
|
||||||
irq_stat = readl(port + PORT_IRQ_STAT);
|
/* Port-wide IRQ mask in HOST_CTRL doesn't really work, clear
|
||||||
writel(irq_stat, port + PORT_IRQ_STAT); /* clear irq */
|
* PORT_IRQ_ENABLE instead.
|
||||||
|
|
||||||
if (!(irq_stat & PORT_IRQ_ERROR)) {
|
|
||||||
/* ignore non-completion, non-error irqs for now */
|
|
||||||
printk(KERN_WARNING DRV_NAME
|
|
||||||
"ata%u: non-error exception irq (irq_stat %x)\n",
|
|
||||||
ap->id, irq_stat);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd_err = readl(port + PORT_CMD_ERR);
|
|
||||||
sstatus = readl(port + PORT_SSTATUS);
|
|
||||||
serror = readl(port + PORT_SERROR);
|
|
||||||
if (serror)
|
|
||||||
writel(serror, port + PORT_SERROR);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Don't log ATAPI device errors. They're supposed to happen
|
|
||||||
* and any serious errors will be logged using sense data by
|
|
||||||
* the SCSI layer.
|
|
||||||
*/
|
*/
|
||||||
if (ap->device[0].class != ATA_DEV_ATAPI || cmd_err > PORT_CERR_SDB)
|
writel(0xffff, port + PORT_IRQ_ENABLE_CLR);
|
||||||
printk("ata%u: error interrupt on port%d\n"
|
}
|
||||||
" stat=0x%x irq=0x%x cmd_err=%d sstatus=0x%x serror=0x%x\n",
|
|
||||||
ap->id, ap->port_no, slot_stat, irq_stat, cmd_err, sstatus, serror);
|
|
||||||
|
|
||||||
if (cmd_err == PORT_CERR_DEV || cmd_err == PORT_CERR_SDB) {
|
static void sil24_thaw(struct ata_port *ap)
|
||||||
/*
|
{
|
||||||
* Device is reporting error, tf registers are valid.
|
void __iomem *port = (void __iomem *)ap->ioaddr.cmd_addr;
|
||||||
|
u32 tmp;
|
||||||
|
|
||||||
|
/* clear IRQ */
|
||||||
|
tmp = readl(port + PORT_IRQ_STAT);
|
||||||
|
writel(tmp, port + PORT_IRQ_STAT);
|
||||||
|
|
||||||
|
/* turn IRQ back on */
|
||||||
|
writel(DEF_PORT_IRQ, port + PORT_IRQ_ENABLE_SET);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sil24_error_intr(struct ata_port *ap)
|
||||||
|
{
|
||||||
|
void __iomem *port = (void __iomem *)ap->ioaddr.cmd_addr;
|
||||||
|
struct ata_eh_info *ehi = &ap->eh_info;
|
||||||
|
int freeze = 0;
|
||||||
|
u32 irq_stat;
|
||||||
|
|
||||||
|
/* on error, we need to clear IRQ explicitly */
|
||||||
|
irq_stat = readl(port + PORT_IRQ_STAT);
|
||||||
|
writel(irq_stat, port + PORT_IRQ_STAT);
|
||||||
|
|
||||||
|
/* first, analyze and record host port events */
|
||||||
|
ata_ehi_clear_desc(ehi);
|
||||||
|
|
||||||
|
ata_ehi_push_desc(ehi, "irq_stat 0x%08x", irq_stat);
|
||||||
|
|
||||||
|
if (irq_stat & PORT_IRQ_DEV_XCHG) {
|
||||||
|
ehi->err_mask |= AC_ERR_ATA_BUS;
|
||||||
|
/* sil24 doesn't recover very well from phy
|
||||||
|
* disconnection with a softreset. Force hardreset.
|
||||||
*/
|
*/
|
||||||
sil24_update_tf(ap);
|
ehi->action |= ATA_EH_HARDRESET;
|
||||||
err_mask = ac_err_mask(pp->tf.command);
|
ata_ehi_push_desc(ehi, ", device_exchanged");
|
||||||
sil24_restart_controller(ap);
|
freeze = 1;
|
||||||
} else {
|
|
||||||
/*
|
|
||||||
* Other errors. libata currently doesn't have any
|
|
||||||
* mechanism to report these errors. Just turn on
|
|
||||||
* ATA_ERR.
|
|
||||||
*/
|
|
||||||
err_mask = AC_ERR_OTHER;
|
|
||||||
sil24_reset_controller(ap);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (qc) {
|
if (irq_stat & PORT_IRQ_UNK_FIS) {
|
||||||
qc->err_mask |= err_mask;
|
ehi->err_mask |= AC_ERR_HSM;
|
||||||
ata_qc_complete(qc);
|
ehi->action |= ATA_EH_SOFTRESET;
|
||||||
|
ata_ehi_push_desc(ehi , ", unknown FIS");
|
||||||
|
freeze = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* deal with command error */
|
||||||
|
if (irq_stat & PORT_IRQ_ERROR) {
|
||||||
|
struct sil24_cerr_info *ci = NULL;
|
||||||
|
unsigned int err_mask = 0, action = 0;
|
||||||
|
struct ata_queued_cmd *qc;
|
||||||
|
u32 cerr;
|
||||||
|
|
||||||
|
/* analyze CMD_ERR */
|
||||||
|
cerr = readl(port + PORT_CMD_ERR);
|
||||||
|
if (cerr < ARRAY_SIZE(sil24_cerr_db))
|
||||||
|
ci = &sil24_cerr_db[cerr];
|
||||||
|
|
||||||
|
if (ci && ci->desc) {
|
||||||
|
err_mask |= ci->err_mask;
|
||||||
|
action |= ci->action;
|
||||||
|
ata_ehi_push_desc(ehi, ", %s", ci->desc);
|
||||||
|
} else {
|
||||||
|
err_mask |= AC_ERR_OTHER;
|
||||||
|
action |= ATA_EH_SOFTRESET;
|
||||||
|
ata_ehi_push_desc(ehi, ", unknown command error %d",
|
||||||
|
cerr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* record error info */
|
||||||
|
qc = ata_qc_from_tag(ap, ap->active_tag);
|
||||||
|
if (qc) {
|
||||||
|
int tag = qc->tag;
|
||||||
|
if (unlikely(ata_tag_internal(tag)))
|
||||||
|
tag = 0;
|
||||||
|
sil24_update_tf(ap);
|
||||||
|
qc->err_mask |= err_mask;
|
||||||
|
} else
|
||||||
|
ehi->err_mask |= err_mask;
|
||||||
|
|
||||||
|
ehi->action |= action;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* freeze or abort */
|
||||||
|
if (freeze)
|
||||||
|
ata_port_freeze(ap);
|
||||||
|
else
|
||||||
|
ata_port_abort(ap);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void sil24_host_intr(struct ata_port *ap)
|
static inline void sil24_host_intr(struct ata_port *ap)
|
||||||
{
|
{
|
||||||
struct ata_queued_cmd *qc = ata_qc_from_tag(ap, ap->active_tag);
|
|
||||||
void __iomem *port = (void __iomem *)ap->ioaddr.cmd_addr;
|
void __iomem *port = (void __iomem *)ap->ioaddr.cmd_addr;
|
||||||
|
struct ata_queued_cmd *qc;
|
||||||
u32 slot_stat;
|
u32 slot_stat;
|
||||||
|
|
||||||
slot_stat = readl(port + PORT_SLOT_STAT);
|
slot_stat = readl(port + PORT_SLOT_STAT);
|
||||||
if (!(slot_stat & HOST_SSTAT_ATTN)) {
|
|
||||||
struct sil24_port_priv *pp = ap->private_data;
|
|
||||||
|
|
||||||
if (ap->flags & SIL24_FLAG_PCIX_IRQ_WOC)
|
if (unlikely(slot_stat & HOST_SSTAT_ATTN)) {
|
||||||
writel(PORT_IRQ_COMPLETE, port + PORT_IRQ_STAT);
|
sil24_error_intr(ap);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (qc) {
|
if (ap->flags & SIL24_FLAG_PCIX_IRQ_WOC)
|
||||||
if (qc->flags & ATA_QCFLAG_RESULT_TF)
|
writel(PORT_IRQ_COMPLETE, port + PORT_IRQ_STAT);
|
||||||
sil24_update_tf(ap);
|
|
||||||
qc->err_mask |= ac_err_mask(pp->tf.command);
|
qc = ata_qc_from_tag(ap, ap->active_tag);
|
||||||
ata_qc_complete(qc);
|
if (qc) {
|
||||||
}
|
if (qc->flags & ATA_QCFLAG_RESULT_TF)
|
||||||
} else
|
sil24_update_tf(ap);
|
||||||
sil24_error_intr(ap, slot_stat);
|
ata_qc_complete(qc);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ata_ratelimit())
|
||||||
|
ata_port_printk(ap, KERN_INFO, "spurious interrupt "
|
||||||
|
"(slot_stat 0x%x active_tag %d)\n",
|
||||||
|
slot_stat, ap->active_tag);
|
||||||
}
|
}
|
||||||
|
|
||||||
static irqreturn_t sil24_interrupt(int irq, void *dev_instance, struct pt_regs *regs)
|
static irqreturn_t sil24_interrupt(int irq, void *dev_instance, struct pt_regs *regs)
|
||||||
|
@ -846,6 +876,31 @@ static irqreturn_t sil24_interrupt(int irq, void *dev_instance, struct pt_regs *
|
||||||
return IRQ_RETVAL(handled);
|
return IRQ_RETVAL(handled);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void sil24_error_handler(struct ata_port *ap)
|
||||||
|
{
|
||||||
|
struct ata_eh_context *ehc = &ap->eh_context;
|
||||||
|
|
||||||
|
if (sil24_init_port(ap)) {
|
||||||
|
ata_eh_freeze_port(ap);
|
||||||
|
ehc->i.action |= ATA_EH_HARDRESET;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* perform recovery */
|
||||||
|
ata_do_eh(ap, sil24_softreset, sil24_hardreset, ata_std_postreset);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sil24_post_internal_cmd(struct ata_queued_cmd *qc)
|
||||||
|
{
|
||||||
|
struct ata_port *ap = qc->ap;
|
||||||
|
|
||||||
|
if (qc->flags & ATA_QCFLAG_FAILED)
|
||||||
|
qc->err_mask |= AC_ERR_OTHER;
|
||||||
|
|
||||||
|
/* make DMA engine forget about the failed command */
|
||||||
|
if (qc->err_mask)
|
||||||
|
sil24_init_port(ap);
|
||||||
|
}
|
||||||
|
|
||||||
static inline void sil24_cblk_free(struct sil24_port_priv *pp, struct device *dev)
|
static inline void sil24_cblk_free(struct sil24_port_priv *pp, struct device *dev)
|
||||||
{
|
{
|
||||||
const size_t cb_size = sizeof(*pp->cmd_block);
|
const size_t cb_size = sizeof(*pp->cmd_block);
|
||||||
|
@ -1058,15 +1113,6 @@ static int sil24_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
|
||||||
/* Always use 64bit activation */
|
/* Always use 64bit activation */
|
||||||
writel(PORT_CS_32BIT_ACTV, port + PORT_CTRL_CLR);
|
writel(PORT_CS_32BIT_ACTV, port + PORT_CTRL_CLR);
|
||||||
|
|
||||||
/* Configure interrupts */
|
|
||||||
writel(0xffff, port + PORT_IRQ_ENABLE_CLR);
|
|
||||||
writel(PORT_IRQ_COMPLETE | PORT_IRQ_ERROR |
|
|
||||||
PORT_IRQ_SDB_NOTIFY, port + PORT_IRQ_ENABLE_SET);
|
|
||||||
|
|
||||||
/* Clear interrupts */
|
|
||||||
writel(0x0fff0fff, port + PORT_IRQ_STAT);
|
|
||||||
writel(PORT_CS_IRQ_WOC, port + PORT_CTRL_CLR);
|
|
||||||
|
|
||||||
/* Clear port multiplier enable and resume bits */
|
/* Clear port multiplier enable and resume bits */
|
||||||
writel(PORT_CS_PM_EN | PORT_CS_RESUME, port + PORT_CTRL_CLR);
|
writel(PORT_CS_PM_EN | PORT_CS_RESUME, port + PORT_CTRL_CLR);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue