*/
#include <linux/ctype.h>
#include <linux/init.h>
-#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/spinlock.h>
#define NAME53C "sym53c"
#define NAME53C8XX "sym53c8xx"
-/* SPARC just has to be different ... */
-#ifdef __sparc__
-#define IRQ_FMT "%s"
-#define IRQ_PRM(x) __irq_itoa(x)
-#else
-#define IRQ_FMT "%d"
-#define IRQ_PRM(x) (x)
-#endif
-
struct sym_driver_setup sym_driver_setup = SYM_LINUX_DRIVER_SETUP;
unsigned int sym_debug_flags = 0;
static char *excl_string;
static char *safe_string;
module_param_named(cmd_per_lun, sym_driver_setup.max_tag, ushort, 0);
-module_param_string(tag_ctrl, sym_driver_setup.tag_ctrl, 100, 0);
module_param_named(burst, sym_driver_setup.burst_order, byte, 0);
module_param_named(led, sym_driver_setup.scsi_led, byte, 0);
module_param_named(diff, sym_driver_setup.scsi_diff, byte, 0);
module_param_named(safe, safe_string, charp, 0);
MODULE_PARM_DESC(cmd_per_lun, "The maximum number of tags to use by default");
-MODULE_PARM_DESC(tag_ctrl, "More detailed control over tags per LUN");
MODULE_PARM_DESC(burst, "Maximum burst. 0 to disable, 255 to read from registers");
MODULE_PARM_DESC(led, "Set to 1 to enable LED support");
MODULE_PARM_DESC(diff, "0 for no differential mode, 1 for BIOS, 2 for always, 3 for not GPIO3");
* Driver private area in the SCSI command structure.
*/
struct sym_ucmd { /* Override the SCSI pointer structure */
- dma_addr_t data_mapping;
- unsigned char data_mapped;
- unsigned char to_do; /* For error handling */
- void (*old_done)(struct scsi_cmnd *); /* For error handling */
- struct completion *eh_done; /* For error handling */
+ struct completion *eh_done; /* SCSI error handling */
};
#define SYM_UCMD_PTR(cmd) ((struct sym_ucmd *)(&(cmd)->SCp))
#define SYM_SOFTC_PTR(cmd) sym_get_hcb(cmd->device->host)
-static void __unmap_scsi_data(struct pci_dev *pdev, struct scsi_cmnd *cmd)
-{
- int dma_dir = cmd->sc_data_direction;
-
- switch(SYM_UCMD_PTR(cmd)->data_mapped) {
- case 2:
- pci_unmap_sg(pdev, cmd->buffer, cmd->use_sg, dma_dir);
- break;
- case 1:
- pci_unmap_single(pdev, SYM_UCMD_PTR(cmd)->data_mapping,
- cmd->request_bufflen, dma_dir);
- break;
- }
- SYM_UCMD_PTR(cmd)->data_mapped = 0;
-}
-
-static dma_addr_t __map_scsi_single_data(struct pci_dev *pdev, struct scsi_cmnd *cmd)
-{
- dma_addr_t mapping;
- int dma_dir = cmd->sc_data_direction;
-
- mapping = pci_map_single(pdev, cmd->request_buffer,
- cmd->request_bufflen, dma_dir);
- if (mapping) {
- SYM_UCMD_PTR(cmd)->data_mapped = 1;
- SYM_UCMD_PTR(cmd)->data_mapping = mapping;
- }
-
- return mapping;
-}
-
-static int __map_scsi_sg_data(struct pci_dev *pdev, struct scsi_cmnd *cmd)
-{
- int use_sg;
- int dma_dir = cmd->sc_data_direction;
-
- use_sg = pci_map_sg(pdev, cmd->buffer, cmd->use_sg, dma_dir);
- if (use_sg > 0) {
- SYM_UCMD_PTR(cmd)->data_mapped = 2;
- SYM_UCMD_PTR(cmd)->data_mapping = use_sg;
- }
-
- return use_sg;
-}
-
-#define unmap_scsi_data(np, cmd) \
- __unmap_scsi_data(np->s.device, cmd)
-#define map_scsi_single_data(np, cmd) \
- __map_scsi_single_data(np->s.device, cmd)
-#define map_scsi_sg_data(np, cmd) \
- __map_scsi_sg_data(np->s.device, cmd)
/*
* Complete a pending CAM CCB.
*/
void sym_xpt_done(struct sym_hcb *np, struct scsi_cmnd *cmd)
{
- unmap_scsi_data(np, cmd);
- cmd->scsi_done(cmd);
-}
+ struct sym_ucmd *ucmd = SYM_UCMD_PTR(cmd);
+ BUILD_BUG_ON(sizeof(struct scsi_pointer) < sizeof(struct sym_ucmd));
-static void sym_xpt_done2(struct sym_hcb *np, struct scsi_cmnd *cmd, int cam_status)
-{
- sym_set_cam_status(cmd, cam_status);
- sym_xpt_done(np, cmd);
-}
+ if (ucmd->eh_done)
+ complete(ucmd->eh_done);
+ scsi_dma_unmap(cmd);
+ cmd->scsi_done(cmd);
+}
/*
* Tell the SCSI layer about a BUS RESET.
}
/*
- * Tell the SCSI layer about a BUS DEVICE RESET message sent.
- */
-void sym_xpt_async_sent_bdr(struct sym_hcb *np, int target)
-{
- printf_notice("%s: TARGET %d has been reset.\n", sym_name(np), target);
-}
-
-/*
* Choose the more appropriate CAM status if
* the IO encountered an extended error.
*/
/*
* Bounce back the sense data to user.
*/
- memset(&cmd->sense_buffer, 0, sizeof(cmd->sense_buffer));
+ memset(cmd->sense_buffer, 0, SCSI_SENSE_BUFFERSIZE);
memcpy(cmd->sense_buffer, cp->sns_bbuf,
- min(sizeof(cmd->sense_buffer),
- (size_t)SYM_SNS_BBUF_LEN));
+ min(SCSI_SENSE_BUFFERSIZE, SYM_SNS_BBUF_LEN));
#if 0
/*
* If the device reports a UNIT ATTENTION condition
*/
cam_status = sym_xerr_cam_status(DID_ERROR, cp->xerr_status);
}
- cmd->resid = resid;
+ scsi_set_resid(cmd, resid);
cmd->result = (drv_status << 24) + (cam_status << 16) + scsi_status;
}
-
-/*
- * Build the scatter/gather array for an I/O.
- */
-
-static int sym_scatter_no_sglist(struct sym_hcb *np, struct sym_ccb *cp, struct scsi_cmnd *cmd)
-{
- struct sym_tblmove *data = &cp->phys.data[SYM_CONF_MAX_SG-1];
- int segment;
- unsigned int len = cmd->request_bufflen;
-
- if (len) {
- dma_addr_t baddr = map_scsi_single_data(np, cmd);
- if (baddr) {
- if (len & 1) {
- struct sym_tcb *tp = &np->target[cp->target];
- if (tp->head.wval & EWS) {
- len++;
- cp->odd_byte_adjustment++;
- }
- }
- cp->data_len = len;
- sym_build_sge(np, data, baddr, len);
- segment = 1;
- } else {
- segment = -2;
- }
- } else {
- segment = 0;
- }
-
- return segment;
-}
-
static int sym_scatter(struct sym_hcb *np, struct sym_ccb *cp, struct scsi_cmnd *cmd)
{
int segment;
- int use_sg = (int) cmd->use_sg;
+ int use_sg;
cp->data_len = 0;
- if (!use_sg)
- segment = sym_scatter_no_sglist(np, cp, cmd);
- else if ((use_sg = map_scsi_sg_data(np, cmd)) > 0) {
- struct scatterlist *scatter = (struct scatterlist *)cmd->buffer;
+ use_sg = scsi_dma_map(cmd);
+ if (use_sg > 0) {
+ struct scatterlist *sg;
struct sym_tcb *tp = &np->target[cp->target];
struct sym_tblmove *data;
if (use_sg > SYM_CONF_MAX_SG) {
- unmap_scsi_data(np, cmd);
+ scsi_dma_unmap(cmd);
return -1;
}
data = &cp->phys.data[SYM_CONF_MAX_SG - use_sg];
- for (segment = 0; segment < use_sg; segment++) {
- dma_addr_t baddr = sg_dma_address(&scatter[segment]);
- unsigned int len = sg_dma_len(&scatter[segment]);
+ scsi_for_each_sg(cmd, sg, use_sg, segment) {
+ dma_addr_t baddr = sg_dma_address(sg);
+ unsigned int len = sg_dma_len(sg);
if ((len & 1) && (tp->head.wval & EWS)) {
len++;
int order;
/*
- * Minimal checkings, so that we will not
- * go outside our tables.
- */
- if (sdev->id == np->myaddr) {
- sym_xpt_done2(np, cmd, DID_NO_CONNECT);
- return 0;
- }
-
- /*
* Retrieve the target descriptor.
*/
tp = &np->target[sdev->id];
*/
switch (dir) {
case DMA_BIDIRECTIONAL:
- printk("%s: got DMA_BIDIRECTIONAL command", sym_name(np));
+ scmd_printk(KERN_INFO, cmd, "got DMA_BIDIRECTIONAL command");
sym_set_cam_status(cmd, DID_ERROR);
goto out_abort;
case DMA_TO_DEVICE:
/*
* PCI BUS error handler.
*/
-void sym_log_bus_error(struct sym_hcb *np)
+void sym_log_bus_error(struct Scsi_Host *shost)
{
- u_short pci_sts;
- pci_read_config_word(np->s.device, PCI_STATUS, &pci_sts);
+ struct sym_data *sym_data = shost_priv(shost);
+ struct pci_dev *pdev = sym_data->pdev;
+ unsigned short pci_sts;
+ pci_read_config_word(pdev, PCI_STATUS, &pci_sts);
if (pci_sts & 0xf900) {
- pci_write_config_word(np->s.device, PCI_STATUS, pci_sts);
- printf("%s: PCI STATUS = 0x%04x\n",
- sym_name(np), pci_sts & 0xf900);
+ pci_write_config_word(pdev, PCI_STATUS, pci_sts);
+ shost_printk(KERN_WARNING, shost,
+ "PCI bus error: status = 0x%04x\n", pci_sts & 0xf900);
}
}
struct sym_ucmd *ucp = SYM_UCMD_PTR(cmd);
int sts = 0;
- cmd->scsi_done = done;
+ cmd->scsi_done = done;
memset(ucp, 0, sizeof(*ucp));
/*
* Shorten our settle_time if needed for
* this command not to time out.
*/
- if (np->s.settle_time_valid && cmd->timeout_per_command) {
- unsigned long tlimit = jiffies + cmd->timeout_per_command;
+ if (np->s.settle_time_valid && cmd->request->timeout) {
+ unsigned long tlimit = jiffies + cmd->request->timeout;
tlimit -= SYM_CONF_TIMER_INTERVAL*2;
if (time_after(np->s.settle_time, tlimit)) {
np->s.settle_time = tlimit;
/*
* Linux entry point of the interrupt handler.
*/
-static irqreturn_t sym53c8xx_intr(int irq, void *dev_id, struct pt_regs * regs)
+static irqreturn_t sym53c8xx_intr(int irq, void *dev_id)
{
- unsigned long flags;
- struct sym_hcb *np = (struct sym_hcb *)dev_id;
+ struct Scsi_Host *shost = dev_id;
+ struct sym_data *sym_data = shost_priv(shost);
+ irqreturn_t result;
+
+ /* Avoid spinloop trying to handle interrupts on frozen device */
+ if (pci_channel_offline(sym_data->pdev))
+ return IRQ_NONE;
if (DEBUG_FLAGS & DEBUG_TINY) printf_debug ("[");
- spin_lock_irqsave(np->s.host->host_lock, flags);
- sym_interrupt(np);
- spin_unlock_irqrestore(np->s.host->host_lock, flags);
+ spin_lock(shost->host_lock);
+ result = sym_interrupt(shost);
+ spin_unlock(shost->host_lock);
if (DEBUG_FLAGS & DEBUG_TINY) printf_debug ("]\n");
- return IRQ_HANDLED;
+ return result;
}
/*
#define SYM_EH_HOST_RESET 3
/*
- * What we will do regarding the involved SCSI command.
- */
-#define SYM_EH_DO_IGNORE 0
-#define SYM_EH_DO_WAIT 2
-
-/*
- * scsi_done() alias when error recovery is in progress.
- */
-static void sym_eh_done(struct scsi_cmnd *cmd)
-{
- struct sym_ucmd *ucmd = SYM_UCMD_PTR(cmd);
- BUILD_BUG_ON(sizeof(struct scsi_pointer) < sizeof(struct sym_ucmd));
-
- cmd->scsi_done = ucmd->old_done;
-
- if (ucmd->to_do == SYM_EH_DO_WAIT)
- complete(ucmd->eh_done);
-}
-
-/*
* Generic method for our eh processing.
* The 'op' argument tells what we have to do.
*/
static int sym_eh_handler(int op, char *opname, struct scsi_cmnd *cmd)
{
- struct sym_hcb *np = SYM_SOFTC_PTR(cmd);
struct sym_ucmd *ucmd = SYM_UCMD_PTR(cmd);
- struct Scsi_Host *host = cmd->device->host;
+ struct Scsi_Host *shost = cmd->device->host;
+ struct sym_data *sym_data = shost_priv(shost);
+ struct pci_dev *pdev = sym_data->pdev;
+ struct sym_hcb *np = sym_data->ncb;
SYM_QUEHEAD *qp;
- int to_do = SYM_EH_DO_IGNORE;
+ int cmd_queued = 0;
int sts = -1;
struct completion eh_done;
- dev_warn(&cmd->device->sdev_gendev, "%s operation started.\n", opname);
+ scmd_printk(KERN_WARNING, cmd, "%s operation started\n", opname);
- spin_lock_irq(host->host_lock);
+ /* We may be in an error condition because the PCI bus
+ * went down. In this case, we need to wait until the
+ * PCI bus is reset, the card is reset, and only then
+ * proceed with the scsi error recovery. There's no
+ * point in hurrying; take a leisurely wait.
+ */
+#define WAIT_FOR_PCI_RECOVERY 35
+ if (pci_channel_offline(pdev)) {
+ int finished_reset = 0;
+ init_completion(&eh_done);
+ spin_lock_irq(shost->host_lock);
+ /* Make sure we didn't race */
+ if (pci_channel_offline(pdev)) {
+ BUG_ON(sym_data->io_reset);
+ sym_data->io_reset = &eh_done;
+ } else {
+ finished_reset = 1;
+ }
+ spin_unlock_irq(shost->host_lock);
+ if (!finished_reset)
+ finished_reset = wait_for_completion_timeout
+ (sym_data->io_reset,
+ WAIT_FOR_PCI_RECOVERY*HZ);
+ spin_lock_irq(shost->host_lock);
+ sym_data->io_reset = NULL;
+ spin_unlock_irq(shost->host_lock);
+ if (!finished_reset)
+ return SCSI_FAILED;
+ }
+
+ spin_lock_irq(shost->host_lock);
/* This one is queued in some place -> to wait for completion */
FOR_EACH_QUEUED_ELEMENT(&np->busy_ccbq, qp) {
struct sym_ccb *cp = sym_que_entry(qp, struct sym_ccb, link_ccbq);
if (cp->cmd == cmd) {
- to_do = SYM_EH_DO_WAIT;
+ cmd_queued = 1;
break;
}
}
- if (to_do == SYM_EH_DO_WAIT) {
- init_completion(&eh_done);
- ucmd->old_done = cmd->scsi_done;
- ucmd->eh_done = &eh_done;
- wmb();
- cmd->scsi_done = sym_eh_done;
- }
-
/* Try to proceed the operation we have been asked for */
sts = -1;
switch(op) {
break;
case SYM_EH_HOST_RESET:
sym_reset_scsi_bus(np, 0);
- sym_start_up (np, 1);
+ sym_start_up(shost, 1);
sts = 0;
break;
default:
}
/* On error, restore everything and cross fingers :) */
- if (sts) {
- cmd->scsi_done = ucmd->old_done;
- to_do = SYM_EH_DO_IGNORE;
- }
-
- ucmd->to_do = to_do;
- spin_unlock_irq(host->host_lock);
+ if (sts)
+ cmd_queued = 0;
- if (to_do == SYM_EH_DO_WAIT) {
+ if (cmd_queued) {
+ init_completion(&eh_done);
+ ucmd->eh_done = &eh_done;
+ spin_unlock_irq(shost->host_lock);
if (!wait_for_completion_timeout(&eh_done, 5*HZ)) {
- ucmd->to_do = SYM_EH_DO_IGNORE;
- wmb();
+ ucmd->eh_done = NULL;
sts = -2;
}
+ } else {
+ spin_unlock_irq(shost->host_lock);
}
+
dev_warn(&cmd->device->sdev_gendev, "%s operation %s.\n", opname,
sts==0 ? "complete" :sts==-2 ? "timed-out" : "failed");
return sts ? SCSI_FAILED : SCSI_SUCCESS;
}
}
-/*
- * Linux select queue depths function
- */
-#define DEF_DEPTH (sym_driver_setup.max_tag)
-#define ALL_TARGETS -2
-#define NO_TARGET -1
-#define ALL_LUNS -2
-#define NO_LUN -1
-
-static int device_queue_depth(struct sym_hcb *np, int target, int lun)
-{
- int c, h, t, u, v;
- char *p = sym_driver_setup.tag_ctrl;
- char *ep;
-
- h = -1;
- t = NO_TARGET;
- u = NO_LUN;
- while ((c = *p++) != 0) {
- v = simple_strtoul(p, &ep, 0);
- switch(c) {
- case '/':
- ++h;
- t = ALL_TARGETS;
- u = ALL_LUNS;
- break;
- case 't':
- if (t != target)
- t = (target == v) ? v : NO_TARGET;
- u = ALL_LUNS;
- break;
- case 'u':
- if (u != lun)
- u = (lun == v) ? v : NO_LUN;
- break;
- case 'q':
- if (h == np->s.unit &&
- (t == ALL_TARGETS || t == target) &&
- (u == ALL_LUNS || u == lun))
- return v;
- break;
- case '-':
- t = ALL_TARGETS;
- u = ALL_LUNS;
- break;
- default:
- break;
- }
- p = ep;
- }
- return DEF_DEPTH;
-}
-
static int sym53c8xx_slave_alloc(struct scsi_device *sdev)
{
struct sym_hcb *np = sym_get_hcb(sdev->host);
struct sym_tcb *tp = &np->target[sdev->id];
struct sym_lcb *lp;
+ unsigned long flags;
+ int error;
if (sdev->id >= SYM_CONF_MAX_TARGET || sdev->lun >= SYM_CONF_MAX_LUN)
return -ENXIO;
- tp->starget = sdev->sdev_target;
+ spin_lock_irqsave(np->s.host->host_lock, flags);
+
/*
* Fail the device init if the device is flagged NOSCAN at BOOT in
* the NVRAM. This may speed up boot and maintain coherency with
if (tp->usrflags & SYM_SCAN_BOOT_DISABLED) {
tp->usrflags &= ~SYM_SCAN_BOOT_DISABLED;
- starget_printk(KERN_INFO, tp->starget,
+ starget_printk(KERN_INFO, sdev->sdev_target,
"Scan at boot disabled in NVRAM\n");
- return -ENXIO;
+ error = -ENXIO;
+ goto out;
}
if (tp->usrflags & SYM_SCAN_LUNS_DISABLED) {
- if (sdev->lun != 0)
- return -ENXIO;
- starget_printk(KERN_INFO, tp->starget,
+ if (sdev->lun != 0) {
+ error = -ENXIO;
+ goto out;
+ }
+ starget_printk(KERN_INFO, sdev->sdev_target,
"Multiple LUNs disabled in NVRAM\n");
}
lp = sym_alloc_lcb(np, sdev->id, sdev->lun);
- if (!lp)
- return -ENOMEM;
+ if (!lp) {
+ error = -ENOMEM;
+ goto out;
+ }
+ if (tp->nlcb == 1)
+ tp->starget = sdev->sdev_target;
spi_min_period(tp->starget) = tp->usr_period;
spi_max_width(tp->starget) = tp->usr_width;
- return 0;
+ error = 0;
+out:
+ spin_unlock_irqrestore(np->s.host->host_lock, flags);
+
+ return error;
}
/*
/*
* Select queue depth from driver setup.
- * Donnot use more than configured by user.
- * Use at least 2.
- * Donnot use more than our maximum.
+ * Do not use more than configured by user.
+ * Use at least 1.
+ * Do not use more than our maximum.
*/
- reqtags = device_queue_depth(np, sdev->id, sdev->lun);
+ reqtags = sym_driver_setup.max_tag;
if (reqtags > tp->usrtags)
reqtags = tp->usrtags;
if (!sdev->tagged_supported)
reqtags = 0;
-#if 1 /* Avoid to locally queue commands for no good reasons */
if (reqtags > SYM_CONF_MAX_TAG)
reqtags = SYM_CONF_MAX_TAG;
- depth_to_use = (reqtags ? reqtags : 2);
-#else
- depth_to_use = (reqtags ? SYM_CONF_MAX_TAG : 2);
-#endif
+ depth_to_use = reqtags ? reqtags : 1;
scsi_adjust_queue_depth(sdev,
- (sdev->tagged_supported ?
- MSG_SIMPLE_TAG : 0),
+ sdev->tagged_supported ? MSG_SIMPLE_TAG : 0,
depth_to_use);
lp->s.scdev_depth = depth_to_use;
sym_tune_dev_queuing(tp, sdev->lun, reqtags);
static void sym53c8xx_slave_destroy(struct scsi_device *sdev)
{
struct sym_hcb *np = sym_get_hcb(sdev->host);
- struct sym_lcb *lp = sym_lp(&np->target[sdev->id], sdev->lun);
+ struct sym_tcb *tp = &np->target[sdev->id];
+ struct sym_lcb *lp = sym_lp(tp, sdev->lun);
+ unsigned long flags;
+
+ spin_lock_irqsave(np->s.host->host_lock, flags);
+
+ if (lp->busy_itlq || lp->busy_itl) {
+ /*
+ * This really shouldn't happen, but we can't return an error
+ * so let's try to stop all on-going I/O.
+ */
+ starget_printk(KERN_WARNING, tp->starget,
+ "Removing busy LCB (%d)\n", sdev->lun);
+ sym_reset_scsi_bus(np, 1);
+ }
- if (lp->itlq_tbl)
- sym_mfree_dma(lp->itlq_tbl, SYM_CONF_MAX_TASK * 4, "ITLQ_TBL");
- kfree(lp->cb_tags);
- sym_mfree_dma(lp, sizeof(*lp), "LCB");
+ if (sym_free_lcb(np, sdev->id, sdev->lun) == 0) {
+ /*
+ * It was the last unit for this target.
+ */
+ tp->head.sval = 0;
+ tp->head.wval = np->rv_scntl3;
+ tp->head.uval = 0;
+ tp->tgoal.check_nego = 1;
+ tp->starget = NULL;
+ }
+
+ spin_unlock_irqrestore(np->s.host->host_lock, flags);
}
/*
if (!((uc->target >> t) & 1))
continue;
tp = &np->target[t];
+ if (!tp->nlcb)
+ continue;
switch (uc->cmd) {
* Parse a control command
*/
-static int sym_user_command(struct sym_hcb *np, char *buffer, int length)
+static int sym_user_command(struct Scsi_Host *shost, char *buffer, int length)
{
+ struct sym_hcb *np = sym_get_hcb(shost);
char *ptr = buffer;
int len = length;
struct sym_usrcmd cmd, *uc = &cmd;
else {
unsigned long flags;
- spin_lock_irqsave(np->s.host->host_lock, flags);
- sym_exec_user_command (np, uc);
- spin_unlock_irqrestore(np->s.host->host_lock, flags);
+ spin_lock_irqsave(shost->host_lock, flags);
+ sym_exec_user_command(np, uc);
+ spin_unlock_irqrestore(shost->host_lock, flags);
}
return length;
}
/*
* Copy formatted information into the input buffer.
*/
-static int sym_host_info(struct sym_hcb *np, char *ptr, off_t offset, int len)
+static int sym_host_info(struct Scsi_Host *shost, char *ptr, off_t offset, int len)
{
+ struct sym_data *sym_data = shost_priv(shost);
+ struct pci_dev *pdev = sym_data->pdev;
+ struct sym_hcb *np = sym_data->ncb;
struct info_str info;
info.buffer = ptr;
info.pos = 0;
copy_info(&info, "Chip " NAME53C "%s, device id 0x%x, "
- "revision id 0x%x\n",
- np->s.chip_name, np->device_id, np->revision_id);
- copy_info(&info, "At PCI address %s, IRQ " IRQ_FMT "\n",
- pci_name(np->s.device), IRQ_PRM(np->s.irq));
+ "revision id 0x%x\n", np->s.chip_name,
+ pdev->device, pdev->revision);
+ copy_info(&info, "At PCI address %s, IRQ %u\n",
+ pci_name(pdev), pdev->irq);
copy_info(&info, "Min. period factor %d, %s SCSI BUS%s\n",
(int) (np->minsync_dt ? np->minsync_dt : np->minsync),
np->maxwide ? "Wide" : "Narrow",
* - func = 0 means read (returns adapter infos)
* - func = 1 means write (not yet merget from sym53c8xx)
*/
-static int sym53c8xx_proc_info(struct Scsi_Host *host, char *buffer,
+static int sym53c8xx_proc_info(struct Scsi_Host *shost, char *buffer,
char **start, off_t offset, int length, int func)
{
- struct sym_hcb *np = sym_get_hcb(host);
int retv;
if (func) {
#ifdef SYM_LINUX_USER_COMMAND_SUPPORT
- retv = sym_user_command(np, buffer, length);
+ retv = sym_user_command(shost, buffer, length);
#else
retv = -EINVAL;
#endif
if (start)
*start = buffer;
#ifdef SYM_LINUX_USER_INFO_SUPPORT
- retv = sym_host_info(np, buffer, offset, length);
+ retv = sym_host_info(shost, buffer, offset, length);
#else
retv = -EINVAL;
#endif
#endif /* SYM_LINUX_PROC_INFO_SUPPORT */
/*
+ * Free resources claimed by sym_iomap_device(). Note that
+ * sym_free_resources() should be used instead of this function after calling
+ * sym_attach().
+ */
+static void __devinit
+sym_iounmap_device(struct sym_device *device)
+{
+ if (device->s.ioaddr)
+ pci_iounmap(device->pdev, device->s.ioaddr);
+ if (device->s.ramaddr)
+ pci_iounmap(device->pdev, device->s.ramaddr);
+}
+
+/*
* Free controller resources.
*/
-static void sym_free_resources(struct sym_hcb *np, struct pci_dev *pdev)
+static void sym_free_resources(struct sym_hcb *np, struct pci_dev *pdev,
+ int do_free_irq)
{
/*
* Free O/S specific resources.
*/
- if (np->s.irq)
- free_irq(np->s.irq, np);
+ if (do_free_irq)
+ free_irq(pdev->irq, np->s.host);
if (np->s.ioaddr)
pci_iounmap(pdev, np->s.ioaddr);
if (np->s.ramaddr)
}
/*
- * Ask/tell the system about DMA addressing.
- */
-static int sym_setup_bus_dma_mask(struct sym_hcb *np)
-{
-#if SYM_CONF_DMA_ADDRESSING_MODE > 0
-#if SYM_CONF_DMA_ADDRESSING_MODE == 1
-#define DMA_DAC_MASK DMA_40BIT_MASK
-#elif SYM_CONF_DMA_ADDRESSING_MODE == 2
-#define DMA_DAC_MASK DMA_64BIT_MASK
-#endif
- if ((np->features & FE_DAC) &&
- !pci_set_dma_mask(np->s.device, DMA_DAC_MASK)) {
- np->use_dac = 1;
- return 0;
- }
-#endif
-
- if (!pci_set_dma_mask(np->s.device, DMA_32BIT_MASK))
- return 0;
-
- printf_warning("%s: No suitable DMA available\n", sym_name(np));
- return -1;
-}
-
-/*
* Host attach and initialisations.
*
* Allocate host data and ncb structure.
static struct Scsi_Host * __devinit sym_attach(struct scsi_host_template *tpnt,
int unit, struct sym_device *dev)
{
- struct host_data *host_data;
+ struct sym_data *sym_data;
struct sym_hcb *np = NULL;
- struct Scsi_Host *instance = NULL;
+ struct Scsi_Host *shost = NULL;
struct pci_dev *pdev = dev->pdev;
unsigned long flags;
struct sym_fw *fw;
+ int do_free_irq = 0;
- printk(KERN_INFO
- "sym%d: <%s> rev 0x%x at pci %s irq " IRQ_FMT "\n",
- unit, dev->chip.name, dev->chip.revision_id,
- pci_name(pdev), IRQ_PRM(pdev->irq));
+ printk(KERN_INFO "sym%d: <%s> rev 0x%x at pci %s irq %u\n",
+ unit, dev->chip.name, pdev->revision, pci_name(pdev),
+ pdev->irq);
/*
* Get the firmware for this chip.
if (!fw)
goto attach_failed;
- /*
- * Allocate host_data structure
- */
- instance = scsi_host_alloc(tpnt, sizeof(*host_data));
- if (!instance)
+ shost = scsi_host_alloc(tpnt, sizeof(*sym_data));
+ if (!shost)
goto attach_failed;
- host_data = (struct host_data *) instance->hostdata;
+ sym_data = shost_priv(shost);
/*
* Allocate immediately the host control block,
np = __sym_calloc_dma(&pdev->dev, sizeof(*np), "HCB");
if (!np)
goto attach_failed;
- np->s.device = pdev;
np->bus_dmat = &pdev->dev; /* Result in 1 DMA pool per HBA */
- host_data->ncb = np;
- np->s.host = instance;
+ sym_data->ncb = np;
+ sym_data->pdev = pdev;
+ np->s.host = shost;
- pci_set_drvdata(pdev, np);
+ pci_set_drvdata(pdev, shost);
/*
* Copy some useful infos to the HCB.
*/
np->hcb_ba = vtobus(np);
np->verbose = sym_driver_setup.verbose;
- np->s.device = pdev;
np->s.unit = unit;
- np->device_id = dev->chip.device_id;
- np->revision_id = dev->chip.revision_id;
np->features = dev->chip.features;
np->clock_divn = dev->chip.nr_divisor;
np->maxoffs = dev->chip.offset_max;
np->maxburst = dev->chip.burst_max;
np->myaddr = dev->host_id;
+ np->mmio_ba = (u32)dev->mmio_base;
+ np->ram_ba = (u32)dev->ram_base;
+ np->s.ioaddr = dev->s.ioaddr;
+ np->s.ramaddr = dev->s.ramaddr;
/*
* Edit its name.
strlcpy(np->s.chip_name, dev->chip.name, sizeof(np->s.chip_name));
sprintf(np->s.inst_name, "sym%d", np->s.unit);
- if (sym_setup_bus_dma_mask(np))
+ if ((SYM_CONF_DMA_ADDRESSING_MODE > 0) && (np->features & FE_DAC) &&
+ !pci_set_dma_mask(pdev, DMA_DAC_MASK)) {
+ set_dac(np);
+ } else if (pci_set_dma_mask(pdev, DMA_BIT_MASK(32))) {
+ printf_warning("%s: No suitable DMA available\n", sym_name(np));
goto attach_failed;
-
- /*
- * Try to map the controller chip to
- * virtual and physical memory.
- */
- np->mmio_ba = (u32)dev->mmio_base;
- np->s.ioaddr = dev->s.ioaddr;
- np->s.ramaddr = dev->s.ramaddr;
- np->s.io_ws = (np->features & FE_IO256) ? 256 : 128;
-
- /*
- * Map on-chip RAM if present and supported.
- */
- if (!(np->features & FE_RAM))
- dev->ram_base = 0;
- if (dev->ram_base) {
- np->ram_ba = (u32)dev->ram_base;
- np->ram_ws = (np->features & FE_RAM8K) ? 8192 : 4096;
}
- if (sym_hcb_attach(instance, fw, dev->nvram))
+ if (sym_hcb_attach(shost, fw, dev->nvram))
goto attach_failed;
/*
* If we synchonize the C code with SCRIPTS on interrupt,
* we do not want to share the INTR line at all.
*/
- if (request_irq(pdev->irq, sym53c8xx_intr, SA_SHIRQ, NAME53C8XX, np)) {
- printf_err("%s: request irq %d failure\n",
+ if (request_irq(pdev->irq, sym53c8xx_intr, IRQF_SHARED, NAME53C8XX,
+ shost)) {
+ printf_err("%s: request irq %u failure\n",
sym_name(np), pdev->irq);
goto attach_failed;
}
- np->s.irq = pdev->irq;
+ do_free_irq = 1;
/*
* After SCSI devices have been opened, we cannot
* reset the bus safely, so we do it here.
*/
- spin_lock_irqsave(instance->host_lock, flags);
+ spin_lock_irqsave(shost->host_lock, flags);
if (sym_reset_scsi_bus(np, 0))
goto reset_failed;
/*
* Start the SCRIPTS.
*/
- sym_start_up (np, 1);
+ sym_start_up(shost, 1);
/*
* Start the timer daemon
* Fill Linux host instance structure
* and return success.
*/
- instance->max_channel = 0;
- instance->this_id = np->myaddr;
- instance->max_id = np->maxwide ? 16 : 8;
- instance->max_lun = SYM_CONF_MAX_LUN;
- instance->unique_id = pci_resource_start(pdev, 0);
- instance->cmd_per_lun = SYM_CONF_MAX_TAG;
- instance->can_queue = (SYM_CONF_MAX_START-2);
- instance->sg_tablesize = SYM_CONF_MAX_SG;
- instance->max_cmd_len = 16;
+ shost->max_channel = 0;
+ shost->this_id = np->myaddr;
+ shost->max_id = np->maxwide ? 16 : 8;
+ shost->max_lun = SYM_CONF_MAX_LUN;
+ shost->unique_id = pci_resource_start(pdev, 0);
+ shost->cmd_per_lun = SYM_CONF_MAX_TAG;
+ shost->can_queue = (SYM_CONF_MAX_START-2);
+ shost->sg_tablesize = SYM_CONF_MAX_SG;
+ shost->max_cmd_len = 16;
BUG_ON(sym2_transport_template == NULL);
- instance->transportt = sym2_transport_template;
+ shost->transportt = sym2_transport_template;
+
+ /* 53c896 rev 1 errata: DMA may not cross 16MB boundary */
+ if (pdev->device == PCI_DEVICE_ID_NCR_53C896 && pdev->revision < 2)
+ shost->dma_boundary = 0xFFFFFF;
- spin_unlock_irqrestore(instance->host_lock, flags);
+ spin_unlock_irqrestore(shost->host_lock, flags);
- return instance;
+ return shost;
reset_failed:
printf_err("%s: FATAL ERROR: CHECK SCSI BUS - CABLES, "
"TERMINATION, DEVICE POWER etc.!\n", sym_name(np));
- spin_unlock_irqrestore(instance->host_lock, flags);
+ spin_unlock_irqrestore(shost->host_lock, flags);
attach_failed:
- if (!instance)
- return NULL;
- printf_info("%s: giving up ...\n", sym_name(np));
+ printf_info("sym%d: giving up ...\n", unit);
if (np)
- sym_free_resources(np, pdev);
- scsi_host_put(instance);
+ sym_free_resources(np, pdev, do_free_irq);
+ else
+ sym_iounmap_device(dev);
+ if (shost)
+ scsi_host_put(shost);
return NULL;
}
static void __devinit sym_get_nvram(struct sym_device *devp, struct sym_nvram *nvp)
{
devp->nvram = nvp;
- devp->device_id = devp->chip.device_id;
nvp->type = 0;
sym_read_nvram(devp, nvp);
{
struct sym_chip *chip;
struct pci_dev *pdev = device->pdev;
- u_char revision;
unsigned long io_port = pci_resource_start(pdev, 0);
int i;
* to our device structure so we can make it match the actual device
* and options.
*/
- pci_read_config_byte(pdev, PCI_CLASS_REVISION, &revision);
- chip = sym_lookup_chip_table(pdev->device, revision);
+ chip = sym_lookup_chip_table(pdev->device, pdev->revision);
if (!chip) {
dev_info(&pdev->dev, "device not supported\n");
return -ENODEV;
}
memcpy(&device->chip, chip, sizeof(device->chip));
- device->chip.revision_id = revision;
return 0;
}
* We must ensure the chip will use WRITE AND INVALIDATE.
* The revision number limit is for now arbitrary.
*/
- if (pdev->device == PCI_DEVICE_ID_NCR_53C896 && chip->revision_id < 0x4) {
+ if (pdev->device == PCI_DEVICE_ID_NCR_53C896 && pdev->revision < 0x4) {
chip->features |= (FE_WRIE | FE_CLSE);
}
}
/*
- * Read and check the PCI configuration for any detected NCR
- * boards and save data for attaching after all boards have
- * been detected.
+ * Map HBA registers and on-chip SRAM (if present).
*/
-static void __devinit
-sym_init_device(struct pci_dev *pdev, struct sym_device *device)
+static int __devinit
+sym_iomap_device(struct sym_device *device)
{
- int i = 2;
+ struct pci_dev *pdev = device->pdev;
struct pci_bus_region bus_addr;
-
- device->host_id = SYM_SETUP_HOST_ID;
- device->pdev = pdev;
+ int i = 2;
pcibios_resource_to_bus(pdev, &bus_addr, &pdev->resource[1]);
device->mmio_base = bus_addr.start;
- /*
- * If the BAR is 64-bit, resource 2 will be occupied by the
- * upper 32 bits
- */
- if (!pdev->resource[i].flags)
- i++;
- pcibios_resource_to_bus(pdev, &bus_addr, &pdev->resource[i]);
- device->ram_base = bus_addr.start;
+ if (device->chip.features & FE_RAM) {
+ /*
+ * If the BAR is 64-bit, resource 2 will be occupied by the
+ * upper 32 bits
+ */
+ if (!pdev->resource[i].flags)
+ i++;
+ pcibios_resource_to_bus(pdev, &bus_addr, &pdev->resource[i]);
+ device->ram_base = bus_addr.start;
+ }
#ifdef CONFIG_SCSI_SYM53C8XX_MMIO
if (device->mmio_base)
if (!device->s.ioaddr)
device->s.ioaddr = pci_iomap(pdev, 0,
pci_resource_len(pdev, 0));
- if (device->ram_base)
+ if (!device->s.ioaddr) {
+ dev_err(&pdev->dev, "could not map registers; giving up.\n");
+ return -EIO;
+ }
+ if (device->ram_base) {
device->s.ramaddr = pci_iomap(pdev, i,
pci_resource_len(pdev, i));
+ if (!device->s.ramaddr) {
+ dev_warn(&pdev->dev,
+ "could not map SRAM; continuing anyway.\n");
+ device->ram_base = 0;
+ }
+ }
+
+ return 0;
}
/*
* Detach the host.
* We have to free resources and halt the NCR chip.
*/
-static int sym_detach(struct sym_hcb *np, struct pci_dev *pdev)
+static int sym_detach(struct Scsi_Host *shost, struct pci_dev *pdev)
{
+ struct sym_hcb *np = sym_get_hcb(shost);
printk("%s: detaching ...\n", sym_name(np));
del_timer_sync(&np->s.timer);
udelay(10);
OUTB(np, nc_istat, 0);
- sym_free_resources(np, pdev);
+ sym_free_resources(np, pdev, 1);
+ scsi_host_put(shost);
return 1;
}
{
struct sym_device sym_dev;
struct sym_nvram nvram;
- struct Scsi_Host *instance;
+ struct Scsi_Host *shost;
+ int do_iounmap = 0;
+ int do_disable_device = 1;
memset(&sym_dev, 0, sizeof(sym_dev));
memset(&nvram, 0, sizeof(nvram));
+ sym_dev.pdev = pdev;
+ sym_dev.host_id = SYM_SETUP_HOST_ID;
if (pci_enable_device(pdev))
goto leave;
if (pci_request_regions(pdev, NAME53C8XX))
goto disable;
- sym_init_device(pdev, &sym_dev);
if (sym_check_supported(&sym_dev))
goto free;
- if (sym_check_raid(&sym_dev))
- goto leave; /* Don't disable the device */
+ if (sym_iomap_device(&sym_dev))
+ goto free;
+ do_iounmap = 1;
+
+ if (sym_check_raid(&sym_dev)) {
+ do_disable_device = 0; /* Don't disable the device */
+ goto free;
+ }
if (sym_set_workarounds(&sym_dev))
goto free;
sym_get_nvram(&sym_dev, &nvram);
- instance = sym_attach(&sym2_template, attach_count, &sym_dev);
- if (!instance)
+ do_iounmap = 0; /* Don't sym_iounmap_device() after sym_attach(). */
+ shost = sym_attach(&sym2_template, attach_count, &sym_dev);
+ if (!shost)
goto free;
- if (scsi_add_host(instance, &pdev->dev))
+ if (scsi_add_host(shost, &pdev->dev))
goto detach;
- scsi_scan_host(instance);
+ scsi_scan_host(shost);
attach_count++;
detach:
sym_detach(pci_get_drvdata(pdev), pdev);
free:
+ if (do_iounmap)
+ sym_iounmap_device(&sym_dev);
pci_release_regions(pdev);
disable:
- pci_disable_device(pdev);
+ if (do_disable_device)
+ pci_disable_device(pdev);
leave:
return -ENODEV;
}
-static void __devexit sym2_remove(struct pci_dev *pdev)
+static void sym2_remove(struct pci_dev *pdev)
{
- struct sym_hcb *np = pci_get_drvdata(pdev);
- struct Scsi_Host *host = np->s.host;
-
- scsi_remove_host(host);
- scsi_host_put(host);
-
- sym_detach(np, pdev);
+ struct Scsi_Host *shost = pci_get_drvdata(pdev);
+ scsi_remove_host(shost);
+ sym_detach(shost, pdev);
pci_release_regions(pdev);
pci_disable_device(pdev);
attach_count--;
}
+/**
+ * sym2_io_error_detected() - called when PCI error is detected
+ * @pdev: pointer to PCI device
+ * @state: current state of the PCI slot
+ */
+static pci_ers_result_t sym2_io_error_detected(struct pci_dev *pdev,
+ enum pci_channel_state state)
+{
+ /* If slot is permanently frozen, turn everything off */
+ if (state == pci_channel_io_perm_failure) {
+ sym2_remove(pdev);
+ return PCI_ERS_RESULT_DISCONNECT;
+ }
+
+ disable_irq(pdev->irq);
+ pci_disable_device(pdev);
+
+ /* Request that MMIO be enabled, so register dump can be taken. */
+ return PCI_ERS_RESULT_CAN_RECOVER;
+}
+
+/**
+ * sym2_io_slot_dump - Enable MMIO and dump debug registers
+ * @pdev: pointer to PCI device
+ */
+static pci_ers_result_t sym2_io_slot_dump(struct pci_dev *pdev)
+{
+ struct Scsi_Host *shost = pci_get_drvdata(pdev);
+
+ sym_dump_registers(shost);
+
+ /* Request a slot reset. */
+ return PCI_ERS_RESULT_NEED_RESET;
+}
+
+/**
+ * sym2_reset_workarounds - hardware-specific work-arounds
+ *
+ * This routine is similar to sym_set_workarounds(), except
+ * that, at this point, we already know that the device was
+ * successfully intialized at least once before, and so most
+ * of the steps taken there are un-needed here.
+ */
+static void sym2_reset_workarounds(struct pci_dev *pdev)
+{
+ u_short status_reg;
+ struct sym_chip *chip;
+
+ chip = sym_lookup_chip_table(pdev->device, pdev->revision);
+
+ /* Work around for errant bit in 895A, in a fashion
+ * similar to what is done in sym_set_workarounds().
+ */
+ pci_read_config_word(pdev, PCI_STATUS, &status_reg);
+ if (!(chip->features & FE_66MHZ) && (status_reg & PCI_STATUS_66MHZ)) {
+ status_reg = PCI_STATUS_66MHZ;
+ pci_write_config_word(pdev, PCI_STATUS, status_reg);
+ pci_read_config_word(pdev, PCI_STATUS, &status_reg);
+ }
+}
+
+/**
+ * sym2_io_slot_reset() - called when the pci bus has been reset.
+ * @pdev: pointer to PCI device
+ *
+ * Restart the card from scratch.
+ */
+static pci_ers_result_t sym2_io_slot_reset(struct pci_dev *pdev)
+{
+ struct Scsi_Host *shost = pci_get_drvdata(pdev);
+ struct sym_hcb *np = sym_get_hcb(shost);
+
+ printk(KERN_INFO "%s: recovering from a PCI slot reset\n",
+ sym_name(np));
+
+ if (pci_enable_device(pdev)) {
+ printk(KERN_ERR "%s: Unable to enable after PCI reset\n",
+ sym_name(np));
+ return PCI_ERS_RESULT_DISCONNECT;
+ }
+
+ pci_set_master(pdev);
+ enable_irq(pdev->irq);
+
+ /* If the chip can do Memory Write Invalidate, enable it */
+ if (np->features & FE_WRIE) {
+ if (pci_set_mwi(pdev))
+ return PCI_ERS_RESULT_DISCONNECT;
+ }
+
+ /* Perform work-arounds, analogous to sym_set_workarounds() */
+ sym2_reset_workarounds(pdev);
+
+ /* Perform host reset only on one instance of the card */
+ if (PCI_FUNC(pdev->devfn) == 0) {
+ if (sym_reset_scsi_bus(np, 0)) {
+ printk(KERN_ERR "%s: Unable to reset scsi host\n",
+ sym_name(np));
+ return PCI_ERS_RESULT_DISCONNECT;
+ }
+ sym_start_up(shost, 1);
+ }
+
+ return PCI_ERS_RESULT_RECOVERED;
+}
+
+/**
+ * sym2_io_resume() - resume normal ops after PCI reset
+ * @pdev: pointer to PCI device
+ *
+ * Called when the error recovery driver tells us that its
+ * OK to resume normal operation. Use completion to allow
+ * halted scsi ops to resume.
+ */
+static void sym2_io_resume(struct pci_dev *pdev)
+{
+ struct Scsi_Host *shost = pci_get_drvdata(pdev);
+ struct sym_data *sym_data = shost_priv(shost);
+
+ spin_lock_irq(shost->host_lock);
+ if (sym_data->io_reset)
+ complete_all(sym_data->io_reset);
+ spin_unlock_irq(shost->host_lock);
+}
+
static void sym2_get_signalling(struct Scsi_Host *shost)
{
struct sym_hcb *np = sym_get_hcb(shost);
{ PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_NCR_53C860,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
{ PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_53C1510,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
+ PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_STORAGE_SCSI<<8, 0xffff00, 0UL },
{ PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_NCR_53C896,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
{ PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_NCR_53C895,
{ PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_NCR_53C875,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
{ PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_NCR_53C1510,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, /* new */
+ PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_STORAGE_SCSI<<8, 0xffff00, 0UL }, /* new */
{ PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_53C895A,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
{ PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_53C875A,
MODULE_DEVICE_TABLE(pci, sym2_id_table);
+static struct pci_error_handlers sym2_err_handler = {
+ .error_detected = sym2_io_error_detected,
+ .mmio_enabled = sym2_io_slot_dump,
+ .slot_reset = sym2_io_slot_reset,
+ .resume = sym2_io_resume,
+};
+
static struct pci_driver sym2_driver = {
.name = NAME53C8XX,
.id_table = sym2_id_table,
.probe = sym2_probe,
- .remove = __devexit_p(sym2_remove),
+ .remove = sym2_remove,
+ .err_handler = &sym2_err_handler,
};
static int __init sym2_init(void)