summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/scsi/aic7xxx/Kconfig.aic7xxx1
-rw-r--r--drivers/scsi/aic7xxx/aic7xxx_osm.c261
2 files changed, 262 insertions, 0 deletions
diff --git a/drivers/scsi/aic7xxx/Kconfig.aic7xxx b/drivers/scsi/aic7xxx/Kconfig.aic7xxx
index 8398e0dd481..ac8de03c9fa 100644
--- a/drivers/scsi/aic7xxx/Kconfig.aic7xxx
+++ b/drivers/scsi/aic7xxx/Kconfig.aic7xxx
@@ -5,6 +5,7 @@
config SCSI_AIC7XXX
tristate "Adaptec AIC7xxx Fast -> U160 support (New Driver)"
depends on (PCI || EISA) && SCSI
+ select SCSI_SPI_ATTRS
---help---
This driver supports all of Adaptec's Fast through Ultra 160 PCI
based SCSI controllers as well as the aic7770 based EISA and VLB
diff --git a/drivers/scsi/aic7xxx/aic7xxx_osm.c b/drivers/scsi/aic7xxx/aic7xxx_osm.c
index 35d6de5a4c9..6b6ee0a52a4 100644
--- a/drivers/scsi/aic7xxx/aic7xxx_osm.c
+++ b/drivers/scsi/aic7xxx/aic7xxx_osm.c
@@ -122,6 +122,10 @@
#include "aic7xxx_osm.h"
#include "aic7xxx_inline.h"
#include <scsi/scsicam.h>
+#include <scsi/scsi_transport.h>
+#include <scsi/scsi_transport_spi.h>
+
+static struct scsi_transport_template *ahc_linux_transport_template = NULL;
/*
* Include aiclib.c as part of our
@@ -1728,6 +1732,8 @@ ahc_linux_register_host(struct ahc_softc *ahc, Scsi_Host_Template *template)
ahc_linux_start_dv(ahc);
ahc_unlock(ahc, &s);
+ host->transportt = ahc_linux_transport_template;
+
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
scsi_add_host(host, (ahc->dev_softc ? &ahc->dev_softc->dev : NULL)); /* XXX handle failure */
scsi_scan_host(host);
@@ -4990,13 +4996,267 @@ ahc_platform_dump_card_state(struct ahc_softc *ahc)
static void ahc_linux_exit(void);
+static void ahc_linux_get_period(struct scsi_target *starget)
+{
+ struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
+ struct ahc_softc *ahc = *((struct ahc_softc **)shost->hostdata);
+ struct ahc_tmode_tstate *tstate;
+ struct ahc_initiator_tinfo *tinfo
+ = ahc_fetch_transinfo(ahc,
+ starget->channel + 'A',
+ shost->this_id, starget->id, &tstate);
+ spi_period(starget) = tinfo->curr.period;
+}
+
+static void ahc_linux_set_period(struct scsi_target *starget, int period)
+{
+ struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
+ struct ahc_softc *ahc = *((struct ahc_softc **)shost->hostdata);
+ struct ahc_tmode_tstate *tstate;
+ struct ahc_initiator_tinfo *tinfo
+ = ahc_fetch_transinfo(ahc,
+ starget->channel + 'A',
+ shost->this_id, starget->id, &tstate);
+ struct ahc_devinfo devinfo;
+ unsigned int ppr_options = tinfo->curr.ppr_options;
+ unsigned long flags;
+ unsigned long offset = tinfo->curr.offset;
+ struct ahc_syncrate *syncrate;
+
+ if (offset == 0)
+ offset = MAX_OFFSET;
+
+ ahc_compile_devinfo(&devinfo, shost->this_id, starget->id, 0,
+ starget->channel + 'A', ROLE_INITIATOR);
+ syncrate = ahc_find_syncrate(ahc, &period, &ppr_options, AHC_SYNCRATE_DT);
+ ahc_lock(ahc, &flags);
+ ahc_set_syncrate(ahc, &devinfo, syncrate, period, offset,
+ ppr_options, AHC_TRANS_GOAL, FALSE);
+ ahc_unlock(ahc, &flags);
+}
+
+static void ahc_linux_get_offset(struct scsi_target *starget)
+{
+ struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
+ struct ahc_softc *ahc = *((struct ahc_softc **)shost->hostdata);
+ struct ahc_tmode_tstate *tstate;
+ struct ahc_initiator_tinfo *tinfo
+ = ahc_fetch_transinfo(ahc,
+ starget->channel + 'A',
+ shost->this_id, starget->id, &tstate);
+ spi_offset(starget) = tinfo->curr.offset;
+}
+
+static void ahc_linux_set_offset(struct scsi_target *starget, int offset)
+{
+ struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
+ struct ahc_softc *ahc = *((struct ahc_softc **)shost->hostdata);
+ struct ahc_tmode_tstate *tstate;
+ struct ahc_initiator_tinfo *tinfo
+ = ahc_fetch_transinfo(ahc,
+ starget->channel + 'A',
+ shost->this_id, starget->id, &tstate);
+ struct ahc_devinfo devinfo;
+ unsigned int ppr_options = 0;
+ unsigned int period = 0;
+ unsigned long flags;
+ struct ahc_syncrate *syncrate = NULL;
+
+ ahc_compile_devinfo(&devinfo, shost->this_id, starget->id, 0,
+ starget->channel + 'A', ROLE_INITIATOR);
+ if (offset != 0) {
+ syncrate = ahc_find_syncrate(ahc, &period, &ppr_options, AHC_SYNCRATE_DT);
+ period = tinfo->curr.period;
+ ppr_options = tinfo->curr.ppr_options;
+ }
+ ahc_lock(ahc, &flags);
+ ahc_set_syncrate(ahc, &devinfo, syncrate, period, offset,
+ ppr_options, AHC_TRANS_GOAL, FALSE);
+ ahc_unlock(ahc, &flags);
+}
+
+static void ahc_linux_get_width(struct scsi_target *starget)
+{
+ struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
+ struct ahc_softc *ahc = *((struct ahc_softc **)shost->hostdata);
+ struct ahc_tmode_tstate *tstate;
+ struct ahc_initiator_tinfo *tinfo
+ = ahc_fetch_transinfo(ahc,
+ starget->channel + 'A',
+ shost->this_id, starget->id, &tstate);
+ spi_width(starget) = tinfo->curr.width;
+}
+
+static void ahc_linux_set_width(struct scsi_target *starget, int width)
+{
+ struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
+ struct ahc_softc *ahc = *((struct ahc_softc **)shost->hostdata);
+ struct ahc_devinfo devinfo;
+ unsigned long flags;
+
+ ahc_compile_devinfo(&devinfo, shost->this_id, starget->id, 0,
+ starget->channel + 'A', ROLE_INITIATOR);
+ ahc_lock(ahc, &flags);
+ ahc_set_width(ahc, &devinfo, width, AHC_TRANS_GOAL, FALSE);
+ ahc_unlock(ahc, &flags);
+}
+
+static void ahc_linux_get_dt(struct scsi_target *starget)
+{
+ struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
+ struct ahc_softc *ahc = *((struct ahc_softc **)shost->hostdata);
+ struct ahc_tmode_tstate *tstate;
+ struct ahc_initiator_tinfo *tinfo
+ = ahc_fetch_transinfo(ahc,
+ starget->channel + 'A',
+ shost->this_id, starget->id, &tstate);
+ spi_dt(starget) = tinfo->curr.ppr_options & MSG_EXT_PPR_DT_REQ;
+}
+
+static void ahc_linux_set_dt(struct scsi_target *starget, int dt)
+{
+ struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
+ struct ahc_softc *ahc = *((struct ahc_softc **)shost->hostdata);
+ struct ahc_tmode_tstate *tstate;
+ struct ahc_initiator_tinfo *tinfo
+ = ahc_fetch_transinfo(ahc,
+ starget->channel + 'A',
+ shost->this_id, starget->id, &tstate);
+ struct ahc_devinfo devinfo;
+ unsigned int ppr_options = tinfo->curr.ppr_options
+ & ~MSG_EXT_PPR_DT_REQ;
+ unsigned int period = tinfo->curr.period;
+ unsigned long flags;
+ struct ahc_syncrate *syncrate;
+
+ ahc_compile_devinfo(&devinfo, shost->this_id, starget->id, 0,
+ starget->channel + 'A', ROLE_INITIATOR);
+ syncrate = ahc_find_syncrate(ahc, &period, &ppr_options,
+ dt ? AHC_SYNCRATE_DT : AHC_SYNCRATE_ULTRA2);
+ ahc_lock(ahc, &flags);
+ ahc_set_syncrate(ahc, &devinfo, syncrate, period, tinfo->curr.offset,
+ ppr_options, AHC_TRANS_GOAL, FALSE);
+ ahc_unlock(ahc, &flags);
+}
+
+static void ahc_linux_get_qas(struct scsi_target *starget)
+{
+ struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
+ struct ahc_softc *ahc = *((struct ahc_softc **)shost->hostdata);
+ struct ahc_tmode_tstate *tstate;
+ struct ahc_initiator_tinfo *tinfo
+ = ahc_fetch_transinfo(ahc,
+ starget->channel + 'A',
+ shost->this_id, starget->id, &tstate);
+ spi_dt(starget) = tinfo->curr.ppr_options & MSG_EXT_PPR_QAS_REQ;
+}
+
+static void ahc_linux_set_qas(struct scsi_target *starget, int qas)
+{
+ struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
+ struct ahc_softc *ahc = *((struct ahc_softc **)shost->hostdata);
+ struct ahc_tmode_tstate *tstate;
+ struct ahc_initiator_tinfo *tinfo
+ = ahc_fetch_transinfo(ahc,
+ starget->channel + 'A',
+ shost->this_id, starget->id, &tstate);
+ struct ahc_devinfo devinfo;
+ unsigned int ppr_options = tinfo->curr.ppr_options
+ & ~MSG_EXT_PPR_QAS_REQ;
+ unsigned int period = tinfo->curr.period;
+ unsigned int dt = ppr_options & MSG_EXT_PPR_DT_REQ;
+ unsigned long flags;
+ struct ahc_syncrate *syncrate;
+
+ if (qas)
+ ppr_options |= MSG_EXT_PPR_QAS_REQ;
+
+ ahc_compile_devinfo(&devinfo, shost->this_id, starget->id, 0,
+ starget->channel + 'A', ROLE_INITIATOR);
+ syncrate = ahc_find_syncrate(ahc, &period, &ppr_options,
+ dt ? AHC_SYNCRATE_DT : AHC_SYNCRATE_ULTRA2);
+ ahc_lock(ahc, &flags);
+ ahc_set_syncrate(ahc, &devinfo, syncrate, period, tinfo->curr.offset,
+ ppr_options, AHC_TRANS_GOAL, FALSE);
+ ahc_unlock(ahc, &flags);
+}
+
+static void ahc_linux_get_iu(struct scsi_target *starget)
+{
+ struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
+ struct ahc_softc *ahc = *((struct ahc_softc **)shost->hostdata);
+ struct ahc_tmode_tstate *tstate;
+ struct ahc_initiator_tinfo *tinfo
+ = ahc_fetch_transinfo(ahc,
+ starget->channel + 'A',
+ shost->this_id, starget->id, &tstate);
+ spi_dt(starget) = tinfo->curr.ppr_options & MSG_EXT_PPR_IU_REQ;
+}
+
+static void ahc_linux_set_iu(struct scsi_target *starget, int iu)
+{
+ struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
+ struct ahc_softc *ahc = *((struct ahc_softc **)shost->hostdata);
+ struct ahc_tmode_tstate *tstate;
+ struct ahc_initiator_tinfo *tinfo
+ = ahc_fetch_transinfo(ahc,
+ starget->channel + 'A',
+ shost->this_id, starget->id, &tstate);
+ struct ahc_devinfo devinfo;
+ unsigned int ppr_options = tinfo->curr.ppr_options
+ & ~MSG_EXT_PPR_IU_REQ;
+ unsigned int period = tinfo->curr.period;
+ unsigned int dt = ppr_options & MSG_EXT_PPR_DT_REQ;
+ unsigned long flags;
+ struct ahc_syncrate *syncrate;
+
+ if (iu)
+ ppr_options |= MSG_EXT_PPR_IU_REQ;
+
+ ahc_compile_devinfo(&devinfo, shost->this_id, starget->id, 0,
+ starget->channel + 'A', ROLE_INITIATOR);
+ syncrate = ahc_find_syncrate(ahc, &period, &ppr_options,
+ dt ? AHC_SYNCRATE_DT : AHC_SYNCRATE_ULTRA2);
+ ahc_lock(ahc, &flags);
+ ahc_set_syncrate(ahc, &devinfo, syncrate, period, tinfo->curr.offset,
+ ppr_options, AHC_TRANS_GOAL, FALSE);
+ ahc_unlock(ahc, &flags);
+}
+
+static struct spi_function_template ahc_linux_transport_functions = {
+ .get_offset = ahc_linux_get_offset,
+ .set_offset = ahc_linux_set_offset,
+ .show_offset = 1,
+ .get_period = ahc_linux_get_period,
+ .set_period = ahc_linux_set_period,
+ .show_period = 1,
+ .get_width = ahc_linux_get_width,
+ .set_width = ahc_linux_set_width,
+ .show_width = 1,
+ .get_dt = ahc_linux_get_dt,
+ .set_dt = ahc_linux_set_dt,
+ .show_dt = 1,
+ .get_iu = ahc_linux_get_iu,
+ .set_iu = ahc_linux_set_iu,
+ .show_iu = 1,
+ .get_qas = ahc_linux_get_qas,
+ .set_qas = ahc_linux_set_qas,
+ .show_qas = 1,
+};
+
+
+
static int __init
ahc_linux_init(void)
{
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
+ ahc_linux_transport_template = spi_attach_transport(&ahc_linux_transport_functions);
+ if (!ahc_linux_transport_template)
+ return -ENODEV;
int rc = ahc_linux_detect(&aic7xxx_driver_template);
if (rc)
return rc;
+ spi_release_transport(ahc_linux_transport_template);
ahc_linux_exit();
return -ENODEV;
#else
@@ -5037,6 +5297,7 @@ ahc_linux_exit(void)
#endif
ahc_linux_pci_exit();
ahc_linux_eisa_exit();
+ spi_release_transport(ahc_linux_transport_template);
}
module_init(ahc_linux_init);