#include <linux/blkdev.h> /* For block_size() */
#include <linux/delay.h> /* For ssleep/msleep */
-/*
- * Lock protecting manipulation of the ahd softc list.
- */
-spinlock_t ahd_list_spinlock;
-
/*
* Bucket size for counting good commands in between bad ones.
*/
*/
uint32_t aic79xx_allow_memio = ~0;
-/*
- * aic79xx_detect() has been run, so register all device arrivals
- * immediately with the system rather than deferring to the sorted
- * attachment performed by aic79xx_detect().
- */
-int aic79xx_detect_complete;
-
/*
* So that we can set how long each device is given as a selection timeout.
* The table of values goes like this:
static aic_option_callback_t ahd_linux_setup_tag_info;
static aic_option_callback_t ahd_linux_setup_iocell_info;
static int aic79xx_setup(char *c);
-static int ahd_linux_next_unit(void);
+
+static int ahd_linux_unit;
+
/****************************** Inlines ***************************************/
static __inline void ahd_linux_unmap_scb(struct ahd_softc*, struct scb*);
#define BUILD_SCSIID(ahd, cmd) \
((((cmd)->device->id << TID_SHIFT) & TID) | (ahd)->our_id)
-/*
- * Try to detect an Adaptec 79XX controller.
- */
-static int
-ahd_linux_detect(struct scsi_host_template *template)
-{
- struct ahd_softc *ahd;
- int found;
- int error = 0;
-
- /*
- * If we've been passed any parameters, process them now.
- */
- if (aic79xx)
- aic79xx_setup(aic79xx);
-
- template->proc_name = "aic79xx";
-
- /*
- * Initialize our softc list lock prior to
- * probing for any adapters.
- */
- ahd_list_lockinit();
-
-#ifdef CONFIG_PCI
- error = ahd_linux_pci_init();
- if (error)
- return error;
-#endif
-
- /*
- * Register with the SCSI layer all
- * controllers we've found.
- */
- found = 0;
- TAILQ_FOREACH(ahd, &ahd_tailq, links) {
-
- if (ahd_linux_register_host(ahd, template) == 0)
- found++;
- }
- aic79xx_detect_complete++;
- return found;
-}
-
/*
* Return a string describing the driver.
*/
struct scsi_host_template aic79xx_driver_template = {
.module = THIS_MODULE,
.name = "aic79xx",
+ .proc_name = "aic79xx",
.proc_info = ahd_linux_proc_info,
.info = ahd_linux_info,
.queuecommand = ahd_linux_queue,
host->max_lun = AHD_NUM_LUNS;
host->max_channel = 0;
host->sg_tablesize = AHD_NSEG;
- ahd_set_unit(ahd, ahd_linux_next_unit());
+ ahd_set_unit(ahd, ahd_linux_unit++);
sprintf(buf, "scsi%d", host->host_no);
new_name = malloc(strlen(buf) + 1, M_DEVBUF, M_NOWAIT);
if (new_name != NULL) {
return ((uint64_t)si.totalram << PAGE_SHIFT);
}
-/*
- * Find the smallest available unit number to use
- * for a new device. We don't just use a static
- * count to handle the "repeated hot-(un)plug"
- * scenario.
- */
-static int
-ahd_linux_next_unit(void)
-{
- struct ahd_softc *ahd;
- int unit;
-
- unit = 0;
-retry:
- TAILQ_FOREACH(ahd, &ahd_tailq, links) {
- if (ahd->unit == unit) {
- unit++;
- goto retry;
- }
- }
- return (unit);
-}
-
/*
* Place the SCSI bus into a known state by either resetting it,
* or forcing transfer negotiations on the next command to any
* are identical to those last reported.
*/
starget = ahd->platform_data->starget[target];
- targ = scsi_transport_target_data(starget);
- if (targ == NULL)
+ if (starget == NULL)
break;
+ targ = scsi_transport_target_data(starget);
target_ppr_options =
(spi_dt(starget) ? MSG_EXT_PPR_DT_REQ : 0)
+ (spi_qas(starget) ? MSG_EXT_PPR_QAS_REQ : 0)
- + (spi_iu(starget) ? MSG_EXT_PPR_IU_REQ : 0);
+ + (spi_iu(starget) ? MSG_EXT_PPR_IU_REQ : 0)
+ + (spi_rd_strm(starget) ? MSG_EXT_PPR_RD_STRM : 0)
+ + (spi_pcomp_en(starget) ? MSG_EXT_PPR_PCOMP_EN : 0)
+ + (spi_rti(starget) ? MSG_EXT_PPR_RTI : 0)
+ + (spi_wr_flow(starget) ? MSG_EXT_PPR_WR_FLOW : 0)
+ + (spi_hold_mcs(starget) ? MSG_EXT_PPR_HOLD_MCS : 0);
if (tinfo->curr.period == spi_period(starget)
&& tinfo->curr.width == spi_width(starget)
spi_dt(starget) = tinfo->curr.ppr_options & MSG_EXT_PPR_DT_REQ ? 1 : 0;
spi_qas(starget) = tinfo->curr.ppr_options & MSG_EXT_PPR_QAS_REQ ? 1 : 0;
spi_iu(starget) = tinfo->curr.ppr_options & MSG_EXT_PPR_IU_REQ ? 1 : 0;
+ spi_rd_strm(starget) = tinfo->curr.ppr_options & MSG_EXT_PPR_RD_STRM ? 1 : 0;
+ spi_pcomp_en(starget) = tinfo->curr.ppr_options & MSG_EXT_PPR_PCOMP_EN ? 1 : 0;
+ spi_rti(starget) = tinfo->curr.ppr_options & MSG_EXT_PPR_RTI ? 1 : 0;
+ spi_wr_flow(starget) = tinfo->curr.ppr_options & MSG_EXT_PPR_WR_FLOW ? 1 : 0;
+ spi_hold_mcs(starget) = tinfo->curr.ppr_options & MSG_EXT_PPR_HOLD_MCS ? 1 : 0;
spi_display_xfer_agreement(starget);
break;
}
return (retval);
}
-static void ahd_linux_exit(void);
-
-static void ahd_linux_set_xferflags(struct scsi_target *starget, unsigned int ppr_options, unsigned int period)
-{
- spi_qas(starget) = (ppr_options & MSG_EXT_PPR_QAS_REQ)? 1 : 0;
- spi_dt(starget) = (ppr_options & MSG_EXT_PPR_DT_REQ)? 1 : 0;
- spi_iu(starget) = (ppr_options & MSG_EXT_PPR_IU_REQ) ? 1 : 0;
- spi_rd_strm(starget) = (ppr_options & MSG_EXT_PPR_RD_STRM) ? 1 : 0;
- spi_wr_flow(starget) = (ppr_options & MSG_EXT_PPR_WR_FLOW) ? 1 : 0;
- spi_pcomp_en(starget) = (ppr_options & MSG_EXT_PPR_PCOMP_EN) ? 1 : 0;
- spi_rti(starget) = (ppr_options & MSG_EXT_PPR_RTI) ? 1 : 0;
- spi_period(starget) = period;
-}
-
static void ahd_linux_set_width(struct scsi_target *starget, int width)
{
struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
ahd_find_syncrate(ahd, &period, &ppr_options,
dt ? AHD_SYNCRATE_MAX : AHD_SYNCRATE_ULTRA2);
- ahd_linux_set_xferflags(starget, ppr_options, period);
-
ahd_lock(ahd, &flags);
ahd_set_syncrate(ahd, &devinfo, period, offset,
ppr_options, AHD_TRANS_GOAL, FALSE);
ahd_find_syncrate(ahd, &period, &ppr_options,
dt ? AHD_SYNCRATE_MAX : AHD_SYNCRATE_ULTRA2);
}
- ahd_linux_set_xferflags(starget, ppr_options, period);
ahd_lock(ahd, &flags);
ahd_set_syncrate(ahd, &devinfo, period, offset, ppr_options,
unsigned int ppr_options = tinfo->goal.ppr_options
& ~MSG_EXT_PPR_DT_REQ;
unsigned int period = tinfo->goal.period;
+ unsigned int width = tinfo->goal.width;
unsigned long flags;
#ifdef AHD_DEBUG
#endif
if (dt) {
ppr_options |= MSG_EXT_PPR_DT_REQ;
- if (period > 9)
- period = 9; /* at least 12.5ns for DT */
+ if (!width)
+ ahd_linux_set_width(starget, 1);
} else {
if (period <= 9)
period = 10; /* If resetting DT, period must be >= 25ns */
ahd_find_syncrate(ahd, &period, &ppr_options,
dt ? AHD_SYNCRATE_MAX : AHD_SYNCRATE_ULTRA2);
- ahd_linux_set_xferflags(starget, ppr_options, period);
-
ahd_lock(ahd, &flags);
ahd_set_syncrate(ahd, &devinfo, period, tinfo->goal.offset,
ppr_options, AHD_TRANS_GOAL, FALSE);
ahd_find_syncrate(ahd, &period, &ppr_options,
dt ? AHD_SYNCRATE_MAX : AHD_SYNCRATE_ULTRA2);
- spi_qas(starget) = (ppr_options & MSG_EXT_PPR_QAS_REQ)? 1 : 0;
-
ahd_lock(ahd, &flags);
ahd_set_syncrate(ahd, &devinfo, period, tinfo->goal.offset,
ppr_options, AHD_TRANS_GOAL, FALSE);
ahd_find_syncrate(ahd, &period, &ppr_options,
dt ? AHD_SYNCRATE_MAX : AHD_SYNCRATE_ULTRA2);
- ahd_linux_set_xferflags(starget, ppr_options, period);
-
ahd_lock(ahd, &flags);
ahd_set_syncrate(ahd, &devinfo, period, tinfo->goal.offset,
ppr_options, AHD_TRANS_GOAL, FALSE);
ahd_find_syncrate(ahd, &period, &ppr_options,
dt ? AHD_SYNCRATE_MAX : AHD_SYNCRATE_ULTRA2);
- spi_rd_strm(starget) = (ppr_options & MSG_EXT_PPR_RD_STRM) ? 1 : 0;
-
ahd_lock(ahd, &flags);
ahd_set_syncrate(ahd, &devinfo, period, tinfo->goal.offset,
ppr_options, AHD_TRANS_GOAL, FALSE);
ahd_find_syncrate(ahd, &period, &ppr_options,
dt ? AHD_SYNCRATE_MAX : AHD_SYNCRATE_ULTRA2);
- spi_wr_flow(starget) = (ppr_options & MSG_EXT_PPR_WR_FLOW) ? 1 : 0;
-
ahd_lock(ahd, &flags);
ahd_set_syncrate(ahd, &devinfo, period, tinfo->goal.offset,
ppr_options, AHD_TRANS_GOAL, FALSE);
ahd_find_syncrate(ahd, &period, &ppr_options,
dt ? AHD_SYNCRATE_MAX : AHD_SYNCRATE_ULTRA2);
- spi_rti(starget) = (ppr_options & MSG_EXT_PPR_RTI) ? 1 : 0;
-
ahd_lock(ahd, &flags);
ahd_set_syncrate(ahd, &devinfo, period, tinfo->goal.offset,
ppr_options, AHD_TRANS_GOAL, FALSE);
ahd_find_syncrate(ahd, &period, &ppr_options,
dt ? AHD_SYNCRATE_MAX : AHD_SYNCRATE_ULTRA2);
- spi_pcomp_en(starget) = (ppr_options & MSG_EXT_PPR_PCOMP_EN) ? 1 : 0;
+ ahd_lock(ahd, &flags);
+ ahd_set_syncrate(ahd, &devinfo, period, tinfo->goal.offset,
+ ppr_options, AHD_TRANS_GOAL, FALSE);
+ ahd_unlock(ahd, &flags);
+}
+
+static void ahd_linux_set_hold_mcs(struct scsi_target *starget, int hold)
+{
+ struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
+ struct ahd_softc *ahd = *((struct ahd_softc **)shost->hostdata);
+ struct ahd_tmode_tstate *tstate;
+ struct ahd_initiator_tinfo *tinfo
+ = ahd_fetch_transinfo(ahd,
+ starget->channel + 'A',
+ shost->this_id, starget->id, &tstate);
+ struct ahd_devinfo devinfo;
+ unsigned int ppr_options = tinfo->goal.ppr_options
+ & ~MSG_EXT_PPR_HOLD_MCS;
+ unsigned int period = tinfo->goal.period;
+ unsigned int dt = ppr_options & MSG_EXT_PPR_DT_REQ;
+ unsigned long flags;
+
+ if (hold)
+ ppr_options |= MSG_EXT_PPR_HOLD_MCS;
+
+ ahd_compile_devinfo(&devinfo, shost->this_id, starget->id, 0,
+ starget->channel + 'A', ROLE_INITIATOR);
+ ahd_find_syncrate(ahd, &period, &ppr_options,
+ dt ? AHD_SYNCRATE_MAX : AHD_SYNCRATE_ULTRA2);
ahd_lock(ahd, &flags);
ahd_set_syncrate(ahd, &devinfo, period, tinfo->goal.offset,
ahd_unlock(ahd, &flags);
}
+
+
static struct spi_function_template ahd_linux_transport_functions = {
.set_offset = ahd_linux_set_offset,
.show_offset = 1,
.show_rti = 1,
.set_pcomp_en = ahd_linux_set_pcomp_en,
.show_pcomp_en = 1,
+ .set_hold_mcs = ahd_linux_set_hold_mcs,
+ .show_hold_mcs = 1,
};
-
-
static int __init
ahd_linux_init(void)
{
- ahd_linux_transport_template = spi_attach_transport(&ahd_linux_transport_functions);
+ int error = 0;
+
+ /*
+ * If we've been passed any parameters, process them now.
+ */
+ if (aic79xx)
+ aic79xx_setup(aic79xx);
+
+ ahd_linux_transport_template =
+ spi_attach_transport(&ahd_linux_transport_functions);
if (!ahd_linux_transport_template)
return -ENODEV;
+
scsi_transport_reserve_target(ahd_linux_transport_template,
sizeof(struct ahd_linux_target));
scsi_transport_reserve_device(ahd_linux_transport_template,
sizeof(struct ahd_linux_device));
- if (ahd_linux_detect(&aic79xx_driver_template) > 0)
- return 0;
- spi_release_transport(ahd_linux_transport_template);
- ahd_linux_exit();
- return -ENODEV;
+
+ error = ahd_linux_pci_init();
+ if (error)
+ spi_release_transport(ahd_linux_transport_template);
+ return error;
}
static void __exit