[SCSI] mvsas : interrupt handling

When a slot is busy, we will not free this slot until slot reset is
completed.  When unplugged the disk, we should release all command
tasks with unplugged port that have been sent.

If MVS_USE_TASKLET is defined, we can enable tasklet. Default is off.

Signed-off-by: Ke Wei <kewei@marvell.com>
Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
This commit is contained in:
Ke Wei 2008-03-27 14:54:23 +08:00 committed by James Bottomley
parent ee1f1c2ef9
commit 1fce5e5da0

View file

@ -1218,10 +1218,63 @@ static void mvs_int_port(struct mvs_info *mvi, int phy_no, u32 events)
static void mvs_int_sata(struct mvs_info *mvi)
{
/* FIXME */
u32 tmp;
void __iomem *regs = mvi->regs;
tmp = mr32(INT_STAT_SRS);
mw32(INT_STAT_SRS, tmp & 0xFFFF);
}
static void mvs_slot_free(struct mvs_info *mvi, struct sas_task *task,
static void mvs_slot_reset(struct mvs_info *mvi, struct sas_task *task,
u32 slot_idx)
{
void __iomem *regs = mvi->regs;
struct domain_device *dev = task->dev;
struct asd_sas_port *sas_port = dev->port;
struct mvs_port *port = mvi->slot_info[slot_idx].port;
u32 reg_set, phy_mask;
if (!sas_protocol_ata(task->task_proto)) {
reg_set = 0;
phy_mask = (port->wide_port_phymap) ? port->wide_port_phymap :
sas_port->phy_mask;
} else {
reg_set = port->taskfileset;
phy_mask = sas_port->phy_mask;
}
mvi->tx[mvi->tx_prod] = cpu_to_le32(TXQ_MODE_I | slot_idx |
(TXQ_CMD_SLOT_RESET << TXQ_CMD_SHIFT) |
(phy_mask << TXQ_PHY_SHIFT) |
(reg_set << TXQ_SRS_SHIFT));
mw32(TX_PROD_IDX, mvi->tx_prod);
mvi->tx_prod = (mvi->tx_prod + 1) & (MVS_CHIP_SLOT_SZ - 1);
}
static int mvs_sata_done(struct mvs_info *mvi, struct sas_task *task,
u32 slot_idx, int err)
{
struct mvs_port *port = mvi->slot_info[slot_idx].port;
struct task_status_struct *tstat = &task->task_status;
struct ata_task_resp *resp = (struct ata_task_resp *)tstat->buf;
int stat = SAM_GOOD;
resp->frame_len = sizeof(struct dev_to_host_fis);
memcpy(&resp->ending_fis[0],
SATA_RECEIVED_D2H_FIS(port->taskfileset),
sizeof(struct dev_to_host_fis));
tstat->buf_valid_size = sizeof(*resp);
if (unlikely(err))
stat = SAS_PROTO_RESPONSE;
return stat;
}
static void mvs_slot_free(struct mvs_info *mvi, u32 rx_desc)
{
u32 slot_idx = rx_desc & RXQ_SLOT_MASK;
mvs_tag_clear(mvi, slot_idx);
}
static void mvs_slot_task_free(struct mvs_info *mvi, struct sas_task *task,
struct mvs_slot_info *slot, u32 slot_idx)
{
if (!sas_protocol_ata(task->task_proto))
@ -1244,38 +1297,58 @@ static void mvs_slot_free(struct mvs_info *mvi, struct sas_task *task,
/* do nothing */
break;
}
list_del(&slot->list);
task->lldd_task = NULL;
slot->task = NULL;
mvs_tag_clear(mvi, slot_idx);
slot->port = NULL;
}
static void mvs_slot_err(struct mvs_info *mvi, struct sas_task *task,
static int mvs_slot_err(struct mvs_info *mvi, struct sas_task *task,
u32 slot_idx)
{
struct mvs_slot_info *slot = &mvi->slot_info[slot_idx];
u64 err_dw0 = *(u32 *) slot->response;
void __iomem *regs = mvi->regs;
u32 tmp;
u32 err_dw0 = le32_to_cpu(*(u32 *) (slot->response));
u32 err_dw1 = le32_to_cpu(*(u32 *) (slot->response + 4));
int stat = SAM_CHECK_COND;
if (err_dw0 & CMD_ISS_STPD)
if (sas_protocol_ata(task->task_proto)) {
tmp = mr32(INT_STAT_SRS);
mw32(INT_STAT_SRS, tmp & 0xFFFF);
}
if (err_dw1 & SLOT_BSY_ERR) {
stat = SAS_QUEUE_FULL;
mvs_slot_reset(mvi, task, slot_idx);
}
switch (task->task_proto) {
case SAS_PROTOCOL_SSP:
break;
case SAS_PROTOCOL_SMP:
break;
case SAS_PROTOCOL_SATA:
case SAS_PROTOCOL_STP:
case SAS_PROTOCOL_SATA | SAS_PROTOCOL_STP:
if (err_dw0 & TFILE_ERR)
stat = mvs_sata_done(mvi, task, slot_idx, 1);
break;
default:
break;
}
mvs_hba_sb_dump(mvi, slot_idx, task->task_proto);
mvs_hexdump(16, (u8 *) slot->response, 0);
return stat;
}
static int mvs_slot_complete(struct mvs_info *mvi, u32 rx_desc)
static int mvs_slot_complete(struct mvs_info *mvi, u32 rx_desc, u32 flags)
{
u32 slot_idx = rx_desc & RXQ_SLOT_MASK;
struct mvs_slot_info *slot = &mvi->slot_info[slot_idx];
struct sas_task *task = slot->task;
struct task_status_struct *tstat = &task->task_status;
struct mvs_port *port = &mvi->port[task->dev->port->id];
struct task_status_struct *tstat;
struct mvs_port *port;
bool aborted;
void *to;
if (unlikely(!task || !task->lldd_task))
return -1;
mvs_hba_cq_dump(mvi);
spin_lock(&task->task_state_lock);
aborted = task->task_state_flags & SAS_TASK_STATE_ABORTED;
if (!aborted) {
@ -1285,22 +1358,27 @@ static int mvs_slot_complete(struct mvs_info *mvi, u32 rx_desc)
}
spin_unlock(&task->task_state_lock);
if (aborted)
if (aborted) {
mvs_slot_task_free(mvi, task, slot, slot_idx);
mvs_slot_free(mvi, rx_desc);
return -1;
}
port = slot->port;
tstat = &task->task_status;
memset(tstat, 0, sizeof(*tstat));
tstat->resp = SAS_TASK_COMPLETE;
if (unlikely(!port->port_attached)) {
tstat->stat = SAS_PHY_DOWN;
if (unlikely(!port->port_attached || flags)) {
mvs_slot_err(mvi, task, slot_idx);
if (!sas_protocol_ata(task->task_proto))
tstat->stat = SAS_PHY_DOWN;
goto out;
}
/* error info record present */
if ((rx_desc & RXQ_ERR) && (*(u64 *) slot->response)) {
tstat->stat = SAM_CHECK_COND;
mvs_slot_err(mvi, task, slot_idx);
if (unlikely((rx_desc & RXQ_ERR) && (*(u64 *) slot->response))) {
tstat->stat = mvs_slot_err(mvi, task, slot_idx);
goto out;
}
@ -1337,21 +1415,7 @@ static int mvs_slot_complete(struct mvs_info *mvi, u32 rx_desc)
case SAS_PROTOCOL_SATA:
case SAS_PROTOCOL_STP:
case SAS_PROTOCOL_SATA | SAS_PROTOCOL_STP: {
struct ata_task_resp *resp =
(struct ata_task_resp *)tstat->buf;
if ((rx_desc & (RXQ_DONE | RXQ_ERR | RXQ_ATTN)) ==
RXQ_DONE)
tstat->stat = SAM_GOOD;
else
tstat->stat = SAM_CHECK_COND;
resp->frame_len = sizeof(struct dev_to_host_fis);
memcpy(&resp->ending_fis[0],
SATA_RECEIVED_D2H_FIS(port->taskfileset),
sizeof(struct dev_to_host_fis));
if (resp->ending_fis[2] & ATA_ERR)
mvs_hexdump(16, resp->ending_fis, 0);
tstat->stat = mvs_sata_done(mvi, task, slot_idx, 0);
break;
}
@ -1361,11 +1425,34 @@ static int mvs_slot_complete(struct mvs_info *mvi, u32 rx_desc)
}
out:
mvs_slot_free(mvi, task, slot, slot_idx);
mvs_slot_task_free(mvi, task, slot, slot_idx);
if (unlikely(tstat->stat != SAS_QUEUE_FULL))
mvs_slot_free(mvi, rx_desc);
spin_unlock(&mvi->lock);
task->task_done(task);
spin_lock(&mvi->lock);
return tstat->stat;
}
static void mvs_release_task(struct mvs_info *mvi, int phy_no)
{
struct list_head *pos, *n;
struct mvs_slot_info *slot;
struct mvs_phy *phy = &mvi->phy[phy_no];
struct mvs_port *port = phy->port;
u32 rx_desc;
if (!port)
return;
list_for_each_safe(pos, n, &port->list) {
slot = container_of(pos, struct mvs_slot_info, list);
rx_desc = (u32) (slot - mvi->slot_info);
mvs_slot_complete(mvi, rx_desc, 1);
}
}
static void mvs_int_full(struct mvs_info *mvi)
{
void __iomem *regs = mvi->regs;
@ -1400,40 +1487,43 @@ static int mvs_int_rx(struct mvs_info *mvi, bool self_clear)
* we don't have to stall the CPU reading that register.
* The actual RX ring is offset by one dword, due to this.
*/
rx_prod_idx = mr32(RX_CONS_IDX) & RX_RING_SZ_MASK;
if (rx_prod_idx == 0xfff) { /* h/w hasn't touched RX ring yet */
mvi->rx_cons = 0xfff;
rx_prod_idx = mvi->rx_cons;
mvi->rx_cons = le32_to_cpu(mvi->rx[0]);
if (mvi->rx_cons == 0xfff) /* h/w hasn't touched RX ring yet */
return 0;
}
/* The CMPL_Q may come late, read from register and try again
* note: if coalescing is enabled,
* it will need to read from register every time for sure
*/
if (mvi->rx_cons == rx_prod_idx)
return 0;
mvi->rx_cons = mr32(RX_CONS_IDX) & RX_RING_SZ_MASK;
if (mvi->rx_cons == 0xfff)
mvi->rx_cons = MVS_RX_RING_SZ - 1;
if (mvi->rx_cons == rx_prod_idx)
return 0;
while (mvi->rx_cons != rx_prod_idx) {
/* increment our internal RX consumer pointer */
mvi->rx_cons = (mvi->rx_cons + 1) & (MVS_RX_RING_SZ - 1);
rx_prod_idx = (rx_prod_idx + 1) & (MVS_RX_RING_SZ - 1);
rx_desc = le32_to_cpu(mvi->rx[mvi->rx_cons + 1]);
mvs_hba_cq_dump(mvi);
rx_desc = le32_to_cpu(mvi->rx[rx_prod_idx + 1]);
if (likely(rx_desc & RXQ_DONE))
mvs_slot_complete(mvi, rx_desc);
mvs_slot_complete(mvi, rx_desc, 0);
if (rx_desc & RXQ_ATTN) {
attn = true;
dev_printk(KERN_DEBUG, &pdev->dev, "ATTN %X\n",
rx_desc);
} else if (rx_desc & RXQ_ERR) {
if (!(rx_desc & RXQ_DONE))
mvs_slot_complete(mvi, rx_desc, 0);
dev_printk(KERN_DEBUG, &pdev->dev, "RXQ_ERR %X\n",
rx_desc);
} else if (rx_desc & RXQ_SLOT_RESET) {
dev_printk(KERN_DEBUG, &pdev->dev, "Slot reset[%X]\n",
rx_desc);
mvs_slot_free(mvi, rx_desc);
}
}
@ -1443,6 +1533,23 @@ static int mvs_int_rx(struct mvs_info *mvi, bool self_clear)
return 0;
}
#ifdef MVS_USE_TASKLET
static void mvs_tasklet(unsigned long data)
{
struct mvs_info *mvi = (struct mvs_info *) data;
unsigned long flags;
spin_lock_irqsave(&mvi->lock, flags);
#ifdef MVS_DISABLE_MSI
mvs_int_full(mvi);
#else
mvs_int_rx(mvi, true);
#endif
spin_unlock_irqrestore(&mvi->lock, flags);
}
#endif
static irqreturn_t mvs_interrupt(int irq, void *opaque)
{
struct mvs_info *mvi = opaque;
@ -1451,18 +1558,21 @@ static irqreturn_t mvs_interrupt(int irq, void *opaque)
stat = mr32(GBL_INT_STAT);
/* clear CMD_CMPLT ASAP */
mw32_f(INT_STAT, CINT_DONE);
if (stat == 0 || stat == 0xffffffff)
return IRQ_NONE;
/* clear CMD_CMPLT ASAP */
mw32_f(INT_STAT, CINT_DONE);
#ifndef MVS_USE_TASKLET
spin_lock(&mvi->lock);
mvs_int_full(mvi);
spin_unlock(&mvi->lock);
#else
tasklet_schedule(&mvi->tasklet);
#endif
return IRQ_HANDLED;
}
@ -1471,12 +1581,15 @@ static irqreturn_t mvs_msi_interrupt(int irq, void *opaque)
{
struct mvs_info *mvi = opaque;
#ifndef MVS_USE_TASKLET
spin_lock(&mvi->lock);
mvs_int_rx(mvi, true);
spin_unlock(&mvi->lock);
#else
tasklet_schedule(&mvi->tasklet);
#endif
return IRQ_HANDLED;
}
#endif