X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=drivers%2Fscsi%2F53c700.c;h=8adef650d300a66d99d840c1386b7883e1323e82;hb=6a77f38946aaee1cd85eeec6cf4229b204c15071;hp=1495f0f8dc008cae716dc9f6650f91327c0cdca6;hpb=87fc8d1bb10cd459024a742c6a10961fefcef18f;p=linux-2.6.git diff --git a/drivers/scsi/53c700.c b/drivers/scsi/53c700.c index 1495f0f8d..8adef650d 100644 --- a/drivers/scsi/53c700.c +++ b/drivers/scsi/53c700.c @@ -176,6 +176,8 @@ STATIC void NCR_700_chip_setup(struct Scsi_Host *host); STATIC void NCR_700_chip_reset(struct Scsi_Host *host); STATIC int NCR_700_slave_configure(struct scsi_device *SDpnt); STATIC void NCR_700_slave_destroy(struct scsi_device *SDpnt); +static int NCR_700_change_queue_depth(struct scsi_device *SDpnt, int depth); +static int NCR_700_change_queue_type(struct scsi_device *SDpnt, int depth); STATIC struct device_attribute *NCR_700_dev_attrs[]; @@ -287,13 +289,15 @@ NCR_700_get_SXFER(struct scsi_device *SDp) struct NCR_700_Host_Parameters *hostdata = (struct NCR_700_Host_Parameters *)SDp->host->hostdata[0]; - return NCR_700_offset_period_to_sxfer(hostdata, spi_offset(SDp), - spi_period(SDp)); + return NCR_700_offset_period_to_sxfer(hostdata, + spi_offset(SDp->sdev_target), + spi_period(SDp->sdev_target)); } struct Scsi_Host * NCR_700_detect(struct scsi_host_template *tpnt, - struct NCR_700_Host_Parameters *hostdata) + struct NCR_700_Host_Parameters *hostdata, struct device *dev, + unsigned long irq, u8 scsi_id) { dma_addr_t pScript, pSlots; __u8 *memory; @@ -320,6 +324,7 @@ NCR_700_detect(struct scsi_host_template *tpnt, * if this isn't sufficient separation to avoid dma flushing issues */ BUG_ON(!dma_is_consistent(pScript) && L1_CACHE_BYTES < dma_get_cache_alignment()); hostdata->slots = (struct NCR_700_command_slot *)(memory + SLOTS_OFFSET); + hostdata->dev = dev; pSlots = pScript + SLOTS_OFFSET; @@ -335,6 +340,8 @@ NCR_700_detect(struct scsi_host_template *tpnt, tpnt->use_clustering = ENABLE_CLUSTERING; tpnt->slave_configure = NCR_700_slave_configure; tpnt->slave_destroy = NCR_700_slave_destroy; + tpnt->change_queue_depth = NCR_700_change_queue_depth; + tpnt->change_queue_type = NCR_700_change_queue_type; if(tpnt->name == NULL) tpnt->name = "53c700"; @@ -386,6 +393,8 @@ NCR_700_detect(struct scsi_host_template *tpnt, host->unique_id = hostdata->base; host->base = hostdata->base; hostdata->eh_complete = NULL; + host->irq = irq; + host->this_id = scsi_id; host->hostdata[0] = (unsigned long)hostdata; /* kick the chip */ NCR_700_writeb(0xff, host, CTEST9_REG); @@ -406,7 +415,28 @@ NCR_700_detect(struct scsi_host_template *tpnt, /* reset the chip */ NCR_700_chip_reset(host); + if (request_irq(irq, NCR_700_intr, SA_SHIRQ, dev->bus_id, host)) { + dev_printk(KERN_ERR, dev, "53c700: irq %lu request failed\n ", + irq); + goto out_put_host; + } + + if (scsi_add_host(host, dev)) { + dev_printk(KERN_ERR, dev, "53c700: scsi_add_host failed\n"); + goto out_release_irq; + } + + spi_signalling(host) = hostdata->differential ? SPI_SIGNAL_HVD : + SPI_SIGNAL_SE; + return host; + + out_release_irq: + free_irq(irq, host); + out_put_host: + scsi_host_put(host); + + return NULL; } int @@ -803,7 +833,7 @@ process_extended_message(struct Scsi_Host *host, } if(NCR_700_is_flag_set(SCp->device, NCR_700_DEV_PRINT_SYNC_NEGOTIATION)) { - if(spi_offset(SCp->device) != 0) + if(spi_offset(SCp->device->sdev_target) != 0) printk(KERN_INFO "scsi%d: (%d:%d) Synchronous at offset %d, period %dns\n", host->host_no, pun, lun, offset, period*4); @@ -813,8 +843,8 @@ process_extended_message(struct Scsi_Host *host, NCR_700_clear_flag(SCp->device, NCR_700_DEV_PRINT_SYNC_NEGOTIATION); } - spi_offset(SCp->device) = offset; - spi_period(SCp->device) = period; + spi_offset(SCp->device->sdev_target) = offset; + spi_period(SCp->device->sdev_target) = period; NCR_700_set_flag(SCp->device, NCR_700_DEV_NEGOTIATED_SYNC); @@ -894,13 +924,15 @@ process_message(struct Scsi_Host *host, struct NCR_700_Host_Parameters *hostdata case A_REJECT_MSG: if(SCp != NULL && NCR_700_is_flag_set(SCp->device, NCR_700_DEV_BEGIN_SYNC_NEGOTIATION)) { /* Rejected our sync negotiation attempt */ - spi_period(SCp->device) = spi_offset(SCp->device) = 0; + spi_period(SCp->device->sdev_target) = + spi_offset(SCp->device->sdev_target) = 0; NCR_700_set_flag(SCp->device, NCR_700_DEV_NEGOTIATED_SYNC); NCR_700_clear_flag(SCp->device, NCR_700_DEV_BEGIN_SYNC_NEGOTIATION); - } else if(SCp != NULL && NCR_700_is_flag_set(SCp->device, NCR_700_DEV_BEGIN_TAG_QUEUEING)) { + } else if(SCp != NULL && NCR_700_get_tag_neg_state(SCp->device) == NCR_700_DURING_TAG_NEGOTIATION) { /* rejected our first simple tag message */ printk(KERN_WARNING "scsi%d (%d:%d) Rejected first tag queue attempt, turning off tag queueing\n", host->host_no, pun, lun); - NCR_700_clear_flag(SCp->device, NCR_700_DEV_BEGIN_TAG_QUEUEING); + /* we're done negotiating */ + NCR_700_set_tag_neg_state(SCp->device, NCR_700_FINISHED_TAG_NEGOTIATION); hostdata->tag_negotiated &= ~(1<device->id); SCp->device->tagged_supported = 0; scsi_deactivate_tcq(SCp->device, host->cmd_per_lun); @@ -962,8 +994,11 @@ process_script_interrupt(__u32 dsps, __u32 dsp, struct scsi_cmnd *SCp, if(dsps == A_GOOD_STATUS_AFTER_STATUS) { DEBUG((" COMMAND COMPLETE, status=%02x\n", hostdata->status[0])); - /* OK, if TCQ still on, we know it works */ - NCR_700_clear_flag(SCp->device, NCR_700_DEV_BEGIN_TAG_QUEUEING); + /* OK, if TCQ still under negotiation, we now know it works */ + if (NCR_700_get_tag_neg_state(SCp->device) == NCR_700_DURING_TAG_NEGOTIATION) + NCR_700_set_tag_neg_state(SCp->device, + NCR_700_FINISHED_TAG_NEGOTIATION); + /* check for contingent allegiance contitions */ if(status_byte(hostdata->status[0]) == CHECK_CONDITION || status_byte(hostdata->status[0]) == COMMAND_TERMINATED) { @@ -1420,8 +1455,8 @@ NCR_700_start_command(struct scsi_cmnd *SCp) NCR_700_is_flag_clear(SCp->device, NCR_700_DEV_NEGOTIATED_SYNC)) { memcpy(&hostdata->msgout[count], NCR_700_SDTR_msg, sizeof(NCR_700_SDTR_msg)); - hostdata->msgout[count+3] = spi_period(SCp->device); - hostdata->msgout[count+4] = spi_offset(SCp->device); + hostdata->msgout[count+3] = spi_period(SCp->device->sdev_target); + hostdata->msgout[count+4] = spi_offset(SCp->device->sdev_target); count += sizeof(NCR_700_SDTR_msg); NCR_700_set_flag(SCp->device, NCR_700_DEV_BEGIN_SYNC_NEGOTIATION); } @@ -1784,23 +1819,12 @@ NCR_700_queuecommand(struct scsi_cmnd *SCp, void (*done)(struct scsi_cmnd *)) printk("53c700: scsi%d, command ", SCp->device->host->host_no); scsi_print_command(SCp); #endif - if(SCp->device->tagged_supported && !SCp->device->simple_tags - && (hostdata->tag_negotiated &(1<device->id)) == 0 - && NCR_700_is_flag_clear(SCp->device, NCR_700_DEV_BEGIN_TAG_QUEUEING)) { - /* upper layer has indicated tags are supported. We don't - * necessarily believe it yet. - * - * NOTE: There is a danger here: the mid layer supports - * tag queuing per LUN. We only support it per PUN because - * of potential reselection issues */ - scsi_activate_tcq(SCp->device, NCR_700_DEFAULT_TAGS); - } - if(blk_rq_tagged(SCp->request) - && (hostdata->tag_negotiated &(1<device->id)) == 0) { - printk(KERN_INFO "scsi%d: (%d:%d) Enabling Tag Command Queuing\n", SCp->device->host->host_no, SCp->device->id, SCp->device->lun); + && (hostdata->tag_negotiated &(1<device->id)) == 0 + && NCR_700_get_tag_neg_state(SCp->device) == NCR_700_START_TAG_NEGOTIATION) { + printk(KERN_ERR "scsi%d: (%d:%d) Enabling Tag Command Queuing\n", SCp->device->host->host_no, SCp->device->id, SCp->device->lun); hostdata->tag_negotiated |= (1<device->id); - NCR_700_set_flag(SCp->device, NCR_700_DEV_BEGIN_TAG_QUEUEING); + NCR_700_set_tag_neg_state(SCp->device, NCR_700_DURING_TAG_NEGOTIATION); } /* here we may have to process an untagged command. The gate @@ -1815,7 +1839,8 @@ NCR_700_queuecommand(struct scsi_cmnd *SCp, void (*done)(struct scsi_cmnd *)) hostdata->tag_negotiated &= ~(1<device->id); } - if((hostdata->tag_negotiated &(1<device->id))) { + if((hostdata->tag_negotiated &(1<device->id)) + && scsi_get_tag_type(SCp->device)) { slot->tag = SCp->request->tag; DEBUG(("53c700 %d:%d:%d, sending out tag %d, slot %p\n", SCp->device->host->host_no, SCp->device->id, SCp->device->lun, slot->tag, @@ -1961,7 +1986,7 @@ NCR_700_bus_reset(struct scsi_cmnd * SCp) * reset via sg or something */ while(hostdata->eh_complete != NULL) { spin_unlock_irq(SCp->device->host->host_lock); - schedule_timeout(HZ/10); + msleep_interruptible(100); spin_lock_irq(SCp->device->host->host_lock); } hostdata->eh_complete = &complete; @@ -1999,10 +2024,11 @@ NCR_700_host_reset(struct scsi_cmnd * SCp) } STATIC void -NCR_700_set_period(struct scsi_device *SDp, int period) +NCR_700_set_period(struct scsi_target *STp, int period) { + struct Scsi_Host *SHp = dev_to_shost(STp->dev.parent); struct NCR_700_Host_Parameters *hostdata = - (struct NCR_700_Host_Parameters *)SDp->host->hostdata[0]; + (struct NCR_700_Host_Parameters *)SHp->hostdata[0]; if(!hostdata->fast) return; @@ -2010,17 +2036,18 @@ NCR_700_set_period(struct scsi_device *SDp, int period) if(period < hostdata->min_period) period = hostdata->min_period; - spi_period(SDp) = period; - NCR_700_clear_flag(SDp, NCR_700_DEV_NEGOTIATED_SYNC); - NCR_700_clear_flag(SDp, NCR_700_DEV_BEGIN_SYNC_NEGOTIATION); - NCR_700_set_flag(SDp, NCR_700_DEV_PRINT_SYNC_NEGOTIATION); + spi_period(STp) = period; + spi_flags(STp) &= ~(NCR_700_DEV_NEGOTIATED_SYNC | + NCR_700_DEV_BEGIN_SYNC_NEGOTIATION); + spi_flags(STp) |= NCR_700_DEV_PRINT_SYNC_NEGOTIATION; } STATIC void -NCR_700_set_offset(struct scsi_device *SDp, int offset) +NCR_700_set_offset(struct scsi_target *STp, int offset) { + struct Scsi_Host *SHp = dev_to_shost(STp->dev.parent); struct NCR_700_Host_Parameters *hostdata = - (struct NCR_700_Host_Parameters *)SDp->host->hostdata[0]; + (struct NCR_700_Host_Parameters *)SHp->hostdata[0]; int max_offset = hostdata->chip710 ? NCR_710_MAX_OFFSET : NCR_700_MAX_OFFSET; @@ -2031,14 +2058,14 @@ NCR_700_set_offset(struct scsi_device *SDp, int offset) offset = max_offset; /* if we're currently async, make sure the period is reasonable */ - if(spi_offset(SDp) == 0 && (spi_period(SDp) < hostdata->min_period || - spi_period(SDp) > 0xff)) - spi_period(SDp) = hostdata->min_period; - - spi_offset(SDp) = offset; - NCR_700_clear_flag(SDp, NCR_700_DEV_NEGOTIATED_SYNC); - NCR_700_clear_flag(SDp, NCR_700_DEV_BEGIN_SYNC_NEGOTIATION); - NCR_700_set_flag(SDp, NCR_700_DEV_PRINT_SYNC_NEGOTIATION); + if(spi_offset(STp) == 0 && (spi_period(STp) < hostdata->min_period || + spi_period(STp) > 0xff)) + spi_period(STp) = hostdata->min_period; + + spi_offset(STp) = offset; + spi_flags(STp) &= ~(NCR_700_DEV_NEGOTIATED_SYNC | + NCR_700_DEV_BEGIN_SYNC_NEGOTIATION); + spi_flags(STp) |= NCR_700_DEV_PRINT_SYNC_NEGOTIATION; } @@ -2051,17 +2078,20 @@ NCR_700_slave_configure(struct scsi_device *SDp) /* to do here: allocate memory; build a queue_full list */ if(SDp->tagged_supported) { - /* do TCQ stuff here */ + scsi_set_tag_type(SDp, MSG_ORDERED_TAG); + scsi_activate_tcq(SDp, NCR_700_DEFAULT_TAGS); + NCR_700_set_tag_neg_state(SDp, NCR_700_START_TAG_NEGOTIATION); } else { /* initialise to default depth */ scsi_adjust_queue_depth(SDp, 0, SDp->host->cmd_per_lun); } if(hostdata->fast) { /* Find the correct offset and period via domain validation */ - spi_dv_device(SDp); + if (!spi_initial_dv(SDp->sdev_target)) + spi_dv_device(SDp); } else { - spi_offset(SDp) = 0; - spi_period(SDp) = 0; + spi_offset(SDp->sdev_target) = 0; + spi_period(SDp->sdev_target) = 0; } return 0; } @@ -2072,18 +2102,48 @@ NCR_700_slave_destroy(struct scsi_device *SDp) /* to do here: deallocate memory */ } -static ssize_t -NCR_700_store_queue_depth(struct device *dev, const char *buf, size_t count) +static int +NCR_700_change_queue_depth(struct scsi_device *SDp, int depth) { - int depth; + if (depth > NCR_700_MAX_TAGS) + depth = NCR_700_MAX_TAGS; - struct scsi_device *SDp = to_scsi_device(dev); - depth = simple_strtoul(buf, NULL, 0); - if(depth > NCR_700_MAX_TAGS) - return -EINVAL; - scsi_adjust_queue_depth(SDp, MSG_ORDERED_TAG, depth); + scsi_adjust_queue_depth(SDp, scsi_get_tag_type(SDp), depth); + return depth; +} - return count; +static int NCR_700_change_queue_type(struct scsi_device *SDp, int tag_type) +{ + int change_tag = ((tag_type ==0 && scsi_get_tag_type(SDp) != 0) + || (tag_type != 0 && scsi_get_tag_type(SDp) == 0)); + struct NCR_700_Host_Parameters *hostdata = + (struct NCR_700_Host_Parameters *)SDp->host->hostdata[0]; + + scsi_set_tag_type(SDp, tag_type); + + /* We have a global (per target) flag to track whether TCQ is + * enabled, so we'll be turning it off for the entire target here. + * our tag algorithm will fail if we mix tagged and untagged commands, + * so quiesce the device before doing this */ + if (change_tag) + scsi_target_quiesce(SDp->sdev_target); + + if (!tag_type) { + /* shift back to the default unqueued number of commands + * (the user can still raise this) */ + scsi_deactivate_tcq(SDp, SDp->host->cmd_per_lun); + hostdata->tag_negotiated &= ~(1 << SDp->id); + } else { + /* Here, we cleared the negotiation flag above, so this + * will force the driver to renegotiate */ + scsi_activate_tcq(SDp, SDp->queue_depth); + if (change_tag) + NCR_700_set_tag_neg_state(SDp, NCR_700_START_TAG_NEGOTIATION); + } + if (change_tag) + scsi_target_resume(SDp->sdev_target); + + return tag_type; } static ssize_t @@ -2094,14 +2154,6 @@ NCR_700_show_active_tags(struct device *dev, char *buf) return snprintf(buf, 20, "%d\n", NCR_700_get_depth(SDp)); } -static struct device_attribute NCR_700_queue_depth_attr = { - .attr = { - .name = "queue_depth", - .mode = S_IWUSR, - }, - .store = NCR_700_store_queue_depth, -}; - static struct device_attribute NCR_700_active_tags_attr = { .attr = { .name = "active_tags", @@ -2111,7 +2163,6 @@ static struct device_attribute NCR_700_active_tags_attr = { }; STATIC struct device_attribute *NCR_700_dev_attrs[] = { - &NCR_700_queue_depth_attr, &NCR_700_active_tags_attr, NULL, };