Commit 9eb46d2a authored by Ed Lin's avatar Ed Lin Committed by James Bottomley

[SCSI] stex: add support for reset request from firmware

Add support for reset request from firmware for controllers
of st_shasta and st_yel type. Code adjustments necessary
for this change are also included.
Signed-off-by: default avatarEd Lin <ed.lin@promise.com>
Signed-off-by: default avatarJames Bottomley <James.Bottomley@suse.de>
parent cbacfb5f
...@@ -64,24 +64,24 @@ enum { ...@@ -64,24 +64,24 @@ enum {
YH2I_REQ_HI = 0xc4, YH2I_REQ_HI = 0xc4,
/* MU register value */ /* MU register value */
MU_INBOUND_DOORBELL_HANDSHAKE = 1, MU_INBOUND_DOORBELL_HANDSHAKE = (1 << 0),
MU_INBOUND_DOORBELL_REQHEADCHANGED = 2, MU_INBOUND_DOORBELL_REQHEADCHANGED = (1 << 1),
MU_INBOUND_DOORBELL_STATUSTAILCHANGED = 4, MU_INBOUND_DOORBELL_STATUSTAILCHANGED = (1 << 2),
MU_INBOUND_DOORBELL_HMUSTOPPED = 8, MU_INBOUND_DOORBELL_HMUSTOPPED = (1 << 3),
MU_INBOUND_DOORBELL_RESET = 16, MU_INBOUND_DOORBELL_RESET = (1 << 4),
MU_OUTBOUND_DOORBELL_HANDSHAKE = 1, MU_OUTBOUND_DOORBELL_HANDSHAKE = (1 << 0),
MU_OUTBOUND_DOORBELL_REQUESTTAILCHANGED = 2, MU_OUTBOUND_DOORBELL_REQUESTTAILCHANGED = (1 << 1),
MU_OUTBOUND_DOORBELL_STATUSHEADCHANGED = 4, MU_OUTBOUND_DOORBELL_STATUSHEADCHANGED = (1 << 2),
MU_OUTBOUND_DOORBELL_BUSCHANGE = 8, MU_OUTBOUND_DOORBELL_BUSCHANGE = (1 << 3),
MU_OUTBOUND_DOORBELL_HASEVENT = 16, MU_OUTBOUND_DOORBELL_HASEVENT = (1 << 4),
MU_OUTBOUND_DOORBELL_REQUEST_RESET = (1 << 27),
/* MU status code */ /* MU status code */
MU_STATE_STARTING = 1, MU_STATE_STARTING = 1,
MU_STATE_FMU_READY_FOR_HANDSHAKE = 2, MU_STATE_STARTED = 2,
MU_STATE_SEND_HANDSHAKE_FRAME = 3, MU_STATE_RESETTING = 3,
MU_STATE_STARTED = 4, MU_STATE_FAILED = 4,
MU_STATE_RESETTING = 5,
MU_MAX_DELAY = 120, MU_MAX_DELAY = 120,
MU_HANDSHAKE_SIGNATURE = 0x55aaaa55, MU_HANDSHAKE_SIGNATURE = 0x55aaaa55,
...@@ -111,6 +111,8 @@ enum { ...@@ -111,6 +111,8 @@ enum {
SS_H2I_INT_RESET = 0x100, SS_H2I_INT_RESET = 0x100,
SS_I2H_REQUEST_RESET = 0x2000,
SS_MU_OPERATIONAL = 0x80000000, SS_MU_OPERATIONAL = 0x80000000,
STEX_CDB_LENGTH = 16, STEX_CDB_LENGTH = 16,
...@@ -312,6 +314,10 @@ struct st_hba { ...@@ -312,6 +314,10 @@ struct st_hba {
struct st_ccb *wait_ccb; struct st_ccb *wait_ccb;
__le32 *scratch; __le32 *scratch;
char work_q_name[20];
struct workqueue_struct *work_q;
struct work_struct reset_work;
wait_queue_head_t reset_waitq;
unsigned int mu_status; unsigned int mu_status;
unsigned int cardtype; unsigned int cardtype;
int msi_enabled; int msi_enabled;
...@@ -578,6 +584,9 @@ stex_queuecommand(struct scsi_cmnd *cmd, void (* done)(struct scsi_cmnd *)) ...@@ -578,6 +584,9 @@ stex_queuecommand(struct scsi_cmnd *cmd, void (* done)(struct scsi_cmnd *))
lun = cmd->device->lun; lun = cmd->device->lun;
hba = (struct st_hba *) &host->hostdata[0]; hba = (struct st_hba *) &host->hostdata[0];
if (unlikely(hba->mu_status == MU_STATE_RESETTING))
return SCSI_MLQUEUE_HOST_BUSY;
switch (cmd->cmnd[0]) { switch (cmd->cmnd[0]) {
case MODE_SENSE_10: case MODE_SENSE_10:
{ {
...@@ -842,7 +851,6 @@ static irqreturn_t stex_intr(int irq, void *__hba) ...@@ -842,7 +851,6 @@ static irqreturn_t stex_intr(int irq, void *__hba)
void __iomem *base = hba->mmio_base; void __iomem *base = hba->mmio_base;
u32 data; u32 data;
unsigned long flags; unsigned long flags;
int handled = 0;
spin_lock_irqsave(hba->host->host_lock, flags); spin_lock_irqsave(hba->host->host_lock, flags);
...@@ -853,12 +861,16 @@ static irqreturn_t stex_intr(int irq, void *__hba) ...@@ -853,12 +861,16 @@ static irqreturn_t stex_intr(int irq, void *__hba)
writel(data, base + ODBL); writel(data, base + ODBL);
readl(base + ODBL); /* flush */ readl(base + ODBL); /* flush */
stex_mu_intr(hba, data); stex_mu_intr(hba, data);
handled = 1; spin_unlock_irqrestore(hba->host->host_lock, flags);
if (unlikely(data & MU_OUTBOUND_DOORBELL_REQUEST_RESET &&
hba->cardtype == st_shasta))
queue_work(hba->work_q, &hba->reset_work);
return IRQ_HANDLED;
} }
spin_unlock_irqrestore(hba->host->host_lock, flags); spin_unlock_irqrestore(hba->host->host_lock, flags);
return IRQ_RETVAL(handled); return IRQ_NONE;
} }
static void stex_ss_mu_intr(struct st_hba *hba) static void stex_ss_mu_intr(struct st_hba *hba)
...@@ -940,7 +952,6 @@ static irqreturn_t stex_ss_intr(int irq, void *__hba) ...@@ -940,7 +952,6 @@ static irqreturn_t stex_ss_intr(int irq, void *__hba)
void __iomem *base = hba->mmio_base; void __iomem *base = hba->mmio_base;
u32 data; u32 data;
unsigned long flags; unsigned long flags;
int handled = 0;
spin_lock_irqsave(hba->host->host_lock, flags); spin_lock_irqsave(hba->host->host_lock, flags);
...@@ -949,12 +960,15 @@ static irqreturn_t stex_ss_intr(int irq, void *__hba) ...@@ -949,12 +960,15 @@ static irqreturn_t stex_ss_intr(int irq, void *__hba)
/* clear the interrupt */ /* clear the interrupt */
writel(data, base + YI2H_INT_C); writel(data, base + YI2H_INT_C);
stex_ss_mu_intr(hba); stex_ss_mu_intr(hba);
handled = 1; spin_unlock_irqrestore(hba->host->host_lock, flags);
if (unlikely(data & SS_I2H_REQUEST_RESET))
queue_work(hba->work_q, &hba->reset_work);
return IRQ_HANDLED;
} }
spin_unlock_irqrestore(hba->host->host_lock, flags); spin_unlock_irqrestore(hba->host->host_lock, flags);
return IRQ_RETVAL(handled); return IRQ_NONE;
} }
static int stex_common_handshake(struct st_hba *hba) static int stex_common_handshake(struct st_hba *hba)
...@@ -1047,7 +1061,7 @@ static int stex_ss_handshake(struct st_hba *hba) ...@@ -1047,7 +1061,7 @@ static int stex_ss_handshake(struct st_hba *hba)
struct st_msg_header *msg_h; struct st_msg_header *msg_h;
struct handshake_frame *h; struct handshake_frame *h;
__le32 *scratch; __le32 *scratch;
u32 data; u32 data, scratch_size;
unsigned long before; unsigned long before;
int ret = 0; int ret = 0;
...@@ -1075,13 +1089,16 @@ static int stex_ss_handshake(struct st_hba *hba) ...@@ -1075,13 +1089,16 @@ static int stex_ss_handshake(struct st_hba *hba)
stex_gettime(&h->hosttime); stex_gettime(&h->hosttime);
h->partner_type = HMU_PARTNER_TYPE; h->partner_type = HMU_PARTNER_TYPE;
h->extra_offset = h->extra_size = 0; h->extra_offset = h->extra_size = 0;
h->scratch_size = cpu_to_le32((hba->sts_count+1)*sizeof(u32)); scratch_size = (hba->sts_count+1)*sizeof(u32);
h->scratch_size = cpu_to_le32(scratch_size);
data = readl(base + YINT_EN); data = readl(base + YINT_EN);
data &= ~4; data &= ~4;
writel(data, base + YINT_EN); writel(data, base + YINT_EN);
writel((hba->dma_handle >> 16) >> 16, base + YH2I_REQ_HI); writel((hba->dma_handle >> 16) >> 16, base + YH2I_REQ_HI);
readl(base + YH2I_REQ_HI);
writel(hba->dma_handle, base + YH2I_REQ); writel(hba->dma_handle, base + YH2I_REQ);
readl(base + YH2I_REQ); /* flush */
scratch = hba->scratch; scratch = hba->scratch;
before = jiffies; before = jiffies;
...@@ -1097,7 +1114,7 @@ static int stex_ss_handshake(struct st_hba *hba) ...@@ -1097,7 +1114,7 @@ static int stex_ss_handshake(struct st_hba *hba)
msleep(1); msleep(1);
} }
*scratch = 0; memset(scratch, 0, scratch_size);
msg_h->flag = 0; msg_h->flag = 0;
return ret; return ret;
} }
...@@ -1106,19 +1123,24 @@ static int stex_handshake(struct st_hba *hba) ...@@ -1106,19 +1123,24 @@ static int stex_handshake(struct st_hba *hba)
{ {
int err; int err;
unsigned long flags; unsigned long flags;
unsigned int mu_status;
err = (hba->cardtype == st_yel) ? err = (hba->cardtype == st_yel) ?
stex_ss_handshake(hba) : stex_common_handshake(hba); stex_ss_handshake(hba) : stex_common_handshake(hba);
spin_lock_irqsave(hba->host->host_lock, flags);
mu_status = hba->mu_status;
if (err == 0) { if (err == 0) {
spin_lock_irqsave(hba->host->host_lock, flags);
hba->req_head = 0; hba->req_head = 0;
hba->req_tail = 0; hba->req_tail = 0;
hba->status_head = 0; hba->status_head = 0;
hba->status_tail = 0; hba->status_tail = 0;
hba->out_req_cnt = 0; hba->out_req_cnt = 0;
hba->mu_status = MU_STATE_STARTED; hba->mu_status = MU_STATE_STARTED;
spin_unlock_irqrestore(hba->host->host_lock, flags); } else
} hba->mu_status = MU_STATE_FAILED;
if (mu_status == MU_STATE_RESETTING)
wake_up_all(&hba->reset_waitq);
spin_unlock_irqrestore(hba->host->host_lock, flags);
return err; return err;
} }
...@@ -1138,17 +1160,11 @@ static int stex_abort(struct scsi_cmnd *cmd) ...@@ -1138,17 +1160,11 @@ static int stex_abort(struct scsi_cmnd *cmd)
base = hba->mmio_base; base = hba->mmio_base;
spin_lock_irqsave(host->host_lock, flags); spin_lock_irqsave(host->host_lock, flags);
if (tag < host->can_queue && hba->ccb[tag].cmd == cmd) if (tag < host->can_queue &&
hba->ccb[tag].req && hba->ccb[tag].cmd == cmd)
hba->wait_ccb = &hba->ccb[tag]; hba->wait_ccb = &hba->ccb[tag];
else { else
for (tag = 0; tag < host->can_queue; tag++) goto out;
if (hba->ccb[tag].cmd == cmd) {
hba->wait_ccb = &hba->ccb[tag];
break;
}
if (tag >= host->can_queue)
goto out;
}
if (hba->cardtype == st_yel) { if (hba->cardtype == st_yel) {
data = readl(base + YI2H_INT); data = readl(base + YI2H_INT);
...@@ -1222,6 +1238,37 @@ static void stex_hard_reset(struct st_hba *hba) ...@@ -1222,6 +1238,37 @@ static void stex_hard_reset(struct st_hba *hba)
hba->pdev->saved_config_space[i]); hba->pdev->saved_config_space[i]);
} }
static int stex_yos_reset(struct st_hba *hba)
{
void __iomem *base;
unsigned long flags, before;
int ret = 0;
base = hba->mmio_base;
writel(MU_INBOUND_DOORBELL_RESET, base + IDBL);
readl(base + IDBL); /* flush */
before = jiffies;
while (hba->out_req_cnt > 0) {
if (time_after(jiffies, before + ST_INTERNAL_TIMEOUT * HZ)) {
printk(KERN_WARNING DRV_NAME
"(%s): reset timeout\n", pci_name(hba->pdev));
ret = -1;
break;
}
msleep(1);
}
spin_lock_irqsave(hba->host->host_lock, flags);
if (ret == -1)
hba->mu_status = MU_STATE_FAILED;
else
hba->mu_status = MU_STATE_STARTED;
wake_up_all(&hba->reset_waitq);
spin_unlock_irqrestore(hba->host->host_lock, flags);
return ret;
}
static void stex_ss_reset(struct st_hba *hba) static void stex_ss_reset(struct st_hba *hba)
{ {
writel(SS_H2I_INT_RESET, hba->mmio_base + YH2I_INT); writel(SS_H2I_INT_RESET, hba->mmio_base + YH2I_INT);
...@@ -1229,66 +1276,86 @@ static void stex_ss_reset(struct st_hba *hba) ...@@ -1229,66 +1276,86 @@ static void stex_ss_reset(struct st_hba *hba)
ssleep(5); ssleep(5);
} }
static int stex_reset(struct scsi_cmnd *cmd) static int stex_do_reset(struct st_hba *hba)
{ {
struct st_hba *hba; struct st_ccb *ccb;
void __iomem *base; unsigned long flags;
unsigned long flags, before; unsigned int mu_status = MU_STATE_RESETTING;
u16 tag;
hba = (struct st_hba *) &cmd->device->host->hostdata[0]; spin_lock_irqsave(hba->host->host_lock, flags);
if (hba->mu_status == MU_STATE_STARTING) {
spin_unlock_irqrestore(hba->host->host_lock, flags);
printk(KERN_INFO DRV_NAME "(%s): request reset during init\n",
pci_name(hba->pdev));
return 0;
}
while (hba->mu_status == MU_STATE_RESETTING) {
spin_unlock_irqrestore(hba->host->host_lock, flags);
wait_event_timeout(hba->reset_waitq,
hba->mu_status != MU_STATE_RESETTING,
MU_MAX_DELAY * HZ);
spin_lock_irqsave(hba->host->host_lock, flags);
mu_status = hba->mu_status;
}
printk(KERN_INFO DRV_NAME if (mu_status != MU_STATE_RESETTING) {
"(%s): resetting host\n", pci_name(hba->pdev)); spin_unlock_irqrestore(hba->host->host_lock, flags);
scsi_print_command(cmd); return (mu_status == MU_STATE_STARTED) ? 0 : -1;
}
hba->mu_status = MU_STATE_RESETTING; hba->mu_status = MU_STATE_RESETTING;
spin_unlock_irqrestore(hba->host->host_lock, flags);
if (hba->cardtype == st_yosemite)
return stex_yos_reset(hba);
if (hba->cardtype == st_shasta) if (hba->cardtype == st_shasta)
stex_hard_reset(hba); stex_hard_reset(hba);
else if (hba->cardtype == st_yel) else if (hba->cardtype == st_yel)
stex_ss_reset(hba); stex_ss_reset(hba);
if (hba->cardtype != st_yosemite) { spin_lock_irqsave(hba->host->host_lock, flags);
if (stex_handshake(hba)) { for (tag = 0; tag < hba->host->can_queue; tag++) {
printk(KERN_WARNING DRV_NAME ccb = &hba->ccb[tag];
"(%s): resetting: handshake failed\n", if (ccb->req == NULL)
pci_name(hba->pdev)); continue;
return FAILED; ccb->req = NULL;
if (ccb->cmd) {
scsi_dma_unmap(ccb->cmd);
ccb->cmd->result = DID_RESET << 16;
ccb->cmd->scsi_done(ccb->cmd);
ccb->cmd = NULL;
} }
return SUCCESS;
} }
spin_unlock_irqrestore(hba->host->host_lock, flags);
/* st_yosemite */ if (stex_handshake(hba) == 0)
writel(MU_INBOUND_DOORBELL_RESET, hba->mmio_base + IDBL); return 0;
readl(hba->mmio_base + IDBL); /* flush */
before = jiffies;
while (hba->out_req_cnt > 0) {
if (time_after(jiffies, before + ST_INTERNAL_TIMEOUT * HZ)) {
printk(KERN_WARNING DRV_NAME
"(%s): reset timeout\n", pci_name(hba->pdev));
return FAILED;
}
msleep(1);
}
base = hba->mmio_base; printk(KERN_WARNING DRV_NAME "(%s): resetting: handshake failed\n",
writel(0, base + IMR0); pci_name(hba->pdev));
readl(base + IMR0); return -1;
writel(0, base + OMR0); }
readl(base + OMR0);
writel(0, base + IMR1); static int stex_reset(struct scsi_cmnd *cmd)
readl(base + IMR1); {
writel(0, base + OMR1); struct st_hba *hba;
readl(base + OMR1); /* flush */
spin_lock_irqsave(hba->host->host_lock, flags); hba = (struct st_hba *) &cmd->device->host->hostdata[0];
hba->req_head = 0;
hba->req_tail = 0; printk(KERN_INFO DRV_NAME
hba->status_head = 0; "(%s): resetting host\n", pci_name(hba->pdev));
hba->status_tail = 0; scsi_print_command(cmd);
hba->out_req_cnt = 0;
hba->mu_status = MU_STATE_STARTED; return stex_do_reset(hba) ? FAILED : SUCCESS;
spin_unlock_irqrestore(hba->host->host_lock, flags); }
return SUCCESS;
static void stex_reset_work(struct work_struct *work)
{
struct st_hba *hba = container_of(work, struct st_hba, reset_work);
stex_do_reset(hba);
} }
static int stex_biosparam(struct scsi_device *sdev, static int stex_biosparam(struct scsi_device *sdev,
...@@ -1583,12 +1650,24 @@ stex_probe(struct pci_dev *pdev, const struct pci_device_id *id) ...@@ -1583,12 +1650,24 @@ stex_probe(struct pci_dev *pdev, const struct pci_device_id *id)
hba->host = host; hba->host = host;
hba->pdev = pdev; hba->pdev = pdev;
init_waitqueue_head(&hba->reset_waitq);
snprintf(hba->work_q_name, sizeof(hba->work_q_name),
"stex_wq_%d", host->host_no);
hba->work_q = create_singlethread_workqueue(hba->work_q_name);
if (!hba->work_q) {
printk(KERN_ERR DRV_NAME "(%s): create workqueue failed\n",
pci_name(pdev));
err = -ENOMEM;
goto out_ccb_free;
}
INIT_WORK(&hba->reset_work, stex_reset_work);
err = stex_request_irq(hba); err = stex_request_irq(hba);
if (err) { if (err) {
printk(KERN_ERR DRV_NAME "(%s): request irq failed\n", printk(KERN_ERR DRV_NAME "(%s): request irq failed\n",
pci_name(pdev)); pci_name(pdev));
goto out_ccb_free; goto out_free_wq;
} }
err = stex_handshake(hba); err = stex_handshake(hba);
...@@ -1617,6 +1696,8 @@ stex_probe(struct pci_dev *pdev, const struct pci_device_id *id) ...@@ -1617,6 +1696,8 @@ stex_probe(struct pci_dev *pdev, const struct pci_device_id *id)
out_free_irq: out_free_irq:
stex_free_irq(hba); stex_free_irq(hba);
out_free_wq:
destroy_workqueue(hba->work_q);
out_ccb_free: out_ccb_free:
kfree(hba->ccb); kfree(hba->ccb);
out_pci_free: out_pci_free:
...@@ -1684,6 +1765,8 @@ static void stex_hba_free(struct st_hba *hba) ...@@ -1684,6 +1765,8 @@ static void stex_hba_free(struct st_hba *hba)
{ {
stex_free_irq(hba); stex_free_irq(hba);
destroy_workqueue(hba->work_q);
iounmap(hba->mmio_base); iounmap(hba->mmio_base);
pci_release_regions(hba->pdev); pci_release_regions(hba->pdev);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment