From: David Milburn <dmilburn@redhat.com> Date: Mon, 30 Nov 2009 20:02:40 -0500 Subject: [scsi] stex: update driver for RHEL-5.5 Message-id: <20091130200240.GA4229@dhcp-210.hsv.redhat.com> Patchwork-id: 21551 O-Subject: [RHEL5.5 PATCH] stex: update driver reset code Bugzilla: 516881 This stex driver update patch is from Ed Lin at Promise, it is based upon this upstream commit commit 69cb48750b02034350bc78d8053647d7464cdde0 Author: Ed Lin <ed.lin@promise.com> Date: Tue Aug 18 12:15:14 2009 -0700 [SCSI] stex: Add reset code for st_yel (v2) And these commits to scsi-misc-2.6: commit cce9c8aed7d3ac0a14815e99b4602ae6c854a0ba Author: Ed Lin <ed.lin@promise.com> Date: Mon Sep 28 22:58:36 2009 -0800 [SCSI] stex: update version to 4.6.0000.4 commit 9eb46d2a08de537e14e92216bf18e7cb541d2f67 Author: Ed Lin <ed.lin@promise.com> Date: Mon Sep 28 22:58:33 2009 -0800 [SCSI] stex: add support for reset request from firmware commit cbacfb5fd9a4689b55157753b8ba4455415fb85c Author: Ed Lin <ed.lin@promise.com> Date: Mon Sep 28 22:58:17 2009 -0800 [SCSI] stex: add small dma buffer support Promise has verified a 2.6.18-169.el5 test kernel built with this patch, and I have stress tested a SuperTrak EX8350/EX16300 with no problems encountered. This resolves BZ 516881, please review and ACK. Thanks, David drivers/scsi/stex.c | 272 ++++++++++++++++++++++++++++++++++++--------------- 1 files changed, 191 insertions(+), 81 deletions(-) Signed-off-by: Don Zickus <dzickus@redhat.com> diff --git a/drivers/scsi/stex.c b/drivers/scsi/stex.c index 685a84c..46105d0 100644 --- a/drivers/scsi/stex.c +++ b/drivers/scsi/stex.c @@ -35,11 +35,11 @@ #include <scsi/scsi_dbg.h> #define DRV_NAME "stex" -#define ST_DRIVER_VERSION "4.6.0102.3" +#define ST_DRIVER_VERSION "4.6.0102.4" #define ST_VER_MAJOR 4 #define ST_VER_MINOR 6 #define ST_OEM 102 -#define ST_BUILD_VER 3 +#define ST_BUILD_VER 4 enum { /* MU register offset */ @@ -54,6 +54,7 @@ enum { OIS = 0x30, /* MU_OUTBOUND_INTERRUPT_STATUS */ OIM = 0x3c, /* MU_OUTBOUND_INTERRUPT_MASK */ + YIOA_STATUS = 0x00, YH2I_INT = 0x20, YINT_EN = 0x34, YI2H_INT = 0x9c, @@ -62,24 +63,24 @@ enum { YH2I_REQ_HI = 0xc4, /* MU register value */ - MU_INBOUND_DOORBELL_HANDSHAKE = 1, - MU_INBOUND_DOORBELL_REQHEADCHANGED = 2, - MU_INBOUND_DOORBELL_STATUSTAILCHANGED = 4, - MU_INBOUND_DOORBELL_HMUSTOPPED = 8, - MU_INBOUND_DOORBELL_RESET = 16, - - MU_OUTBOUND_DOORBELL_HANDSHAKE = 1, - MU_OUTBOUND_DOORBELL_REQUESTTAILCHANGED = 2, - MU_OUTBOUND_DOORBELL_STATUSHEADCHANGED = 4, - MU_OUTBOUND_DOORBELL_BUSCHANGE = 8, - MU_OUTBOUND_DOORBELL_HASEVENT = 16, + MU_INBOUND_DOORBELL_HANDSHAKE = (1 << 0), + MU_INBOUND_DOORBELL_REQHEADCHANGED = (1 << 1), + MU_INBOUND_DOORBELL_STATUSTAILCHANGED = (1 << 2), + MU_INBOUND_DOORBELL_HMUSTOPPED = (1 << 3), + MU_INBOUND_DOORBELL_RESET = (1 << 4), + + MU_OUTBOUND_DOORBELL_HANDSHAKE = (1 << 0), + MU_OUTBOUND_DOORBELL_REQUESTTAILCHANGED = (1 << 1), + MU_OUTBOUND_DOORBELL_STATUSHEADCHANGED = (1 << 2), + MU_OUTBOUND_DOORBELL_BUSCHANGE = (1 << 3), + MU_OUTBOUND_DOORBELL_HASEVENT = (1 << 4), + MU_OUTBOUND_DOORBELL_REQUEST_RESET = (1 << 27), /* MU status code */ MU_STATE_STARTING = 1, - MU_STATE_FMU_READY_FOR_HANDSHAKE = 2, - MU_STATE_SEND_HANDSHAKE_FRAME = 3, - MU_STATE_STARTED = 4, - MU_STATE_RESETTING = 5, + MU_STATE_STARTED = 2, + MU_STATE_RESETTING = 3, + MU_STATE_FAILED = 4, MU_MAX_DELAY = 120, MU_HANDSHAKE_SIGNATURE = 0x55aaaa55, @@ -107,6 +108,12 @@ enum { SS_HEAD_HANDSHAKE = 0x80, + SS_H2I_INT_RESET = 0x100, + + SS_I2H_REQUEST_RESET = 0x2000, + + SS_MU_OPERATIONAL = 0x80000000, + STEX_CDB_LENGTH = 16, STATUS_VAR_LEN = 128, @@ -307,6 +314,10 @@ struct st_hba { struct st_ccb *wait_ccb; __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 cardtype; int msi_enabled; @@ -672,6 +683,9 @@ stex_queuecommand(struct scsi_cmnd *cmd, void (* done)(struct scsi_cmnd *)) lun = cmd->device->lun; hba = (struct st_hba *) &host->hostdata[0]; + if (unlikely(hba->mu_status == MU_STATE_RESETTING)) + return SCSI_MLQUEUE_HOST_BUSY; + switch (cmd->cmnd[0]) { case MODE_SENSE_10: { @@ -950,7 +964,6 @@ static irqreturn_t stex_intr(int irq, void *__hba, struct pt_regs *regs) void __iomem *base = hba->mmio_base; u32 data; unsigned long flags; - int handled = 0; spin_lock_irqsave(hba->host->host_lock, flags); @@ -961,12 +974,16 @@ static irqreturn_t stex_intr(int irq, void *__hba, struct pt_regs *regs) writel(data, base + ODBL); readl(base + ODBL); /* flush */ 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); - return IRQ_RETVAL(handled); + return IRQ_NONE; } static void stex_ss_mu_intr(struct st_hba *hba) @@ -1050,7 +1067,6 @@ static irqreturn_t stex_ss_intr(int irq, void *__hba, struct pt_regs *regs) void __iomem *base = hba->mmio_base; u32 data; unsigned long flags; - int handled = 0; spin_lock_irqsave(hba->host->host_lock, flags); @@ -1059,12 +1075,15 @@ static irqreturn_t stex_ss_intr(int irq, void *__hba, struct pt_regs *regs) /* clear the interrupt */ writel(data, base + YI2H_INT_C); 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); - return IRQ_RETVAL(handled); + return IRQ_NONE; } static int stex_common_handshake(struct st_hba *hba) @@ -1112,7 +1131,7 @@ static int stex_common_handshake(struct st_hba *hba) h->partner_type = HMU_PARTNER_TYPE; if (hba->extra_offset) { h->extra_offset = cpu_to_le32(hba->extra_offset); - h->extra_size = cpu_to_le32(ST_ADDITIONAL_MEM); + h->extra_size = cpu_to_le32(hba->dma_size - hba->extra_offset); } else h->extra_offset = h->extra_size = 0; @@ -1157,15 +1176,26 @@ static int stex_ss_handshake(struct st_hba *hba) struct st_msg_header *msg_h; struct handshake_frame *h; __le32 *scratch; - u32 data; + u32 data, scratch_size; unsigned long before; int ret = 0; - h = (struct handshake_frame *)(hba->alloc_rq(hba)); - msg_h = (struct st_msg_header *)h - 1; + before = jiffies; + while ((readl(base + YIOA_STATUS) & SS_MU_OPERATIONAL) == 0) { + if (time_after(jiffies, before + MU_MAX_DELAY * HZ)) { + printk(KERN_ERR DRV_NAME + "(%s): firmware not operational\n", + pci_name(hba->pdev)); + return -1; + } + msleep(1); + } + + msg_h = (struct st_msg_header *)hba->dma_mem; msg_h->handle = cpu_to_le64(hba->dma_handle); msg_h->flag = SS_HEAD_HANDSHAKE; + h = (struct handshake_frame *)(msg_h + 1); h->rb_phy = cpu_to_le64(hba->dma_handle); h->req_sz = cpu_to_le16(hba->rq_size); h->req_cnt = cpu_to_le16(hba->rq_count+1); @@ -1174,13 +1204,16 @@ static int stex_ss_handshake(struct st_hba *hba) stex_gettime(&h->hosttime); h->partner_type = HMU_PARTNER_TYPE; 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 &= ~4; writel(data, base + YINT_EN); writel((hba->dma_handle >> 16) >> 16, base + YH2I_REQ_HI); + readl(base + YH2I_REQ_HI); writel(hba->dma_handle, base + YH2I_REQ); + readl(base + YH2I_REQ); /* flush */ scratch = hba->scratch; before = jiffies; @@ -1196,7 +1229,7 @@ static int stex_ss_handshake(struct st_hba *hba) msleep(1); } - *scratch = 0; + memset(scratch, 0, scratch_size); msg_h->flag = 0; return ret; } @@ -1205,13 +1238,15 @@ static int stex_handshake(struct st_hba *hba) { int err, size; unsigned long flags; + unsigned int mu_status; err = (hba->cardtype == st_yel) ? stex_ss_handshake(hba) : stex_common_handshake(hba); + spin_lock_irqsave(hba->host->host_lock, flags); + mu_status = hba->mu_status; if (err == 0) { size = ALIGN(hba->host->can_queue, BITS_PER_LONG)/BITS_PER_LONG; size *= sizeof(unsigned long); - spin_lock_irqsave(hba->host->host_lock, flags); hba->req_head = 0; hba->req_tail = 0; hba->status_head = 0; @@ -1219,8 +1254,11 @@ static int stex_handshake(struct st_hba *hba) hba->out_req_cnt = 0; memset(hba->tag, 0, size); 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; } @@ -1241,7 +1279,7 @@ static int stex_abort(struct scsi_cmnd *cmd) base = hba->mmio_base; spin_lock_irqsave(host->host_lock, flags); for (tag = 0; tag < host->can_queue; tag++) - if (hba->ccb[tag].cmd == cmd) { + if (hba->ccb[tag].req && hba->ccb[tag].cmd == cmd) { hba->wait_ccb = &hba->ccb[tag]; break; } @@ -1320,68 +1358,124 @@ static void stex_hard_reset(struct st_hba *hba) hba->pdev->saved_config_space[i]); } -static int stex_reset(struct scsi_cmnd *cmd) +static int stex_yos_reset(struct st_hba *hba) { - struct st_hba *hba; void __iomem *base; unsigned long flags, before; - int size; - - hba = (struct st_hba *) &cmd->device->host->hostdata[0]; - - printk(KERN_INFO DRV_NAME - "(%s): resetting host\n", pci_name(hba->pdev)); - scsi_print_command(cmd); - - hba->mu_status = MU_STATE_RESETTING; - - if (hba->cardtype == st_shasta) - stex_hard_reset(hba); - - if (hba->cardtype != st_yosemite) { - if (stex_handshake(hba)) { - printk(KERN_WARNING DRV_NAME - "(%s): resetting: handshake failed\n", - pci_name(hba->pdev)); - return FAILED; - } - return SUCCESS; - } + int ret = 0; - /* st_yosemite */ - writel(MU_INBOUND_DOORBELL_RESET, hba->mmio_base + IDBL); - readl(hba->mmio_base + IDBL); /* flush */ + 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)); - return FAILED; + ret = -1; + break; } msleep(1); } - base = hba->mmio_base; - writel(0, base + IMR0); - readl(base + IMR0); - writel(0, base + OMR0); - readl(base + OMR0); - writel(0, base + IMR1); - readl(base + IMR1); - writel(0, base + OMR1); - readl(base + OMR1); /* flush */ - size = ALIGN(hba->host->can_queue, BITS_PER_LONG)/BITS_PER_LONG; - size *= sizeof(unsigned long); spin_lock_irqsave(hba->host->host_lock, flags); - hba->req_head = 0; - hba->req_tail = 0; - hba->status_head = 0; - hba->status_tail = 0; - hba->out_req_cnt = 0; - memset(hba->tag, 0, size); - hba->mu_status = MU_STATE_STARTED; + 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) +{ + writel(SS_H2I_INT_RESET, hba->mmio_base + YH2I_INT); + readl(hba->mmio_base + YH2I_INT); + ssleep(5); +} + +static int stex_do_reset(struct st_hba *hba) +{ + struct st_ccb *ccb; + unsigned long flags; + unsigned int mu_status = MU_STATE_RESETTING; + u16 tag; + + 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; + } + + if (mu_status != MU_STATE_RESETTING) { + spin_unlock_irqrestore(hba->host->host_lock, flags); + return (mu_status == MU_STATE_STARTED) ? 0 : -1; + } + + hba->mu_status = MU_STATE_RESETTING; spin_unlock_irqrestore(hba->host->host_lock, flags); - return SUCCESS; + + if (hba->cardtype == st_yosemite) + return stex_yos_reset(hba); + + if (hba->cardtype == st_shasta) + stex_hard_reset(hba); + else if (hba->cardtype == st_yel) + stex_ss_reset(hba); + + spin_lock_irqsave(hba->host->host_lock, flags); + for (tag = 0; tag < hba->host->can_queue; tag++) { + ccb = &hba->ccb[tag]; + if (ccb->req == NULL) + continue; + ccb->req = NULL; + if (ccb->cmd) { + stex_unmap_sg(hba, ccb->cmd); + ccb->cmd->result = DID_RESET << 16; + ccb->cmd->scsi_done(ccb->cmd); + ccb->cmd = NULL; + } + } + spin_unlock_irqrestore(hba->host->host_lock, flags); + + if (stex_handshake(hba) == 0) + return 0; + + printk(KERN_WARNING DRV_NAME "(%s): resetting: handshake failed\n", + pci_name(hba->pdev)); + return -1; +} + +static int stex_reset(struct scsi_cmnd *cmd) +{ + struct st_hba *hba; + + hba = (struct st_hba *) &cmd->device->host->hostdata[0]; + + printk(KERN_INFO DRV_NAME + "(%s): resetting host\n", pci_name(hba->pdev)); + scsi_print_command(cmd); + + return stex_do_reset(hba) ? FAILED : SUCCESS; +} + +static void stex_reset_work(void *data) +{ + struct st_hba *hba = data; + + stex_do_reset(hba); } static int stex_biosparam(struct scsi_device *sdev, @@ -1680,12 +1774,24 @@ stex_probe(struct pci_dev *pdev, const struct pci_device_id *id) hba->host = host; 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, hba); err = stex_request_irq(hba); if (err) { printk(KERN_ERR DRV_NAME "(%s): request irq failed\n", pci_name(pdev)); - goto out_ccb_free; + goto out_free_wq; } err = stex_handshake(hba); @@ -1707,6 +1813,8 @@ stex_probe(struct pci_dev *pdev, const struct pci_device_id *id) out_free_irq: stex_free_irq(hba); +out_free_wq: + destroy_workqueue(hba->work_q); out_ccb_free: kfree(hba->ccb); out_pci_free: @@ -1785,6 +1893,8 @@ static void stex_hba_free(struct st_hba *hba) { stex_free_irq(hba); + destroy_workqueue(hba->work_q); + iounmap(hba->mmio_base); pci_release_regions(hba->pdev);