summaryrefslogtreecommitdiffstats
path: root/drivers/fc4
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/fc4')
-rw-r--r--drivers/fc4/Config.in17
-rw-r--r--drivers/fc4/Makefile39
-rw-r--r--drivers/fc4/fc.c688
-rw-r--r--drivers/fc4/fc.h222
-rw-r--r--drivers/fc4/fc_syms.c28
-rw-r--r--drivers/fc4/fcp.h94
-rw-r--r--drivers/fc4/fcp_scsi.h140
-rw-r--r--drivers/fc4/soc.c710
-rw-r--r--drivers/fc4/soc.h275
9 files changed, 2213 insertions, 0 deletions
diff --git a/drivers/fc4/Config.in b/drivers/fc4/Config.in
new file mode 100644
index 000000000..52597561e
--- /dev/null
+++ b/drivers/fc4/Config.in
@@ -0,0 +1,17 @@
+#
+# FC4 device configuration
+#
+mainmenu_option next_comment
+comment 'Fibre Channel support'
+
+tristate 'Fibre Channel and FC4 SCSI support' CONFIG_FC4 m
+if [ ! "$CONFIG_FC4" = "n" ]; then
+ comment 'FC4 drivers'
+ tristate 'Sun SOC/Sbus' CONFIG_FC4_SOC m
+ comment 'FC4 targets'
+ dep_tristate 'SparcSTORAGE Array 100 and 200 series' CONFIG_SCSI_PLUTO "$CONFIG_SCSI"
+else
+ define_bool CONFIG_FC4_SOC n
+ define_bool CONFIG_SCSI_PLUTO n
+fi
+endmenu
diff --git a/drivers/fc4/Makefile b/drivers/fc4/Makefile
new file mode 100644
index 000000000..3ecfe0f83
--- /dev/null
+++ b/drivers/fc4/Makefile
@@ -0,0 +1,39 @@
+# File: drivers/fc4/Makefile
+#
+# Makefile for the Linux Fibre Channel device drivers.
+#
+
+L_TARGET := fc4.a
+M_OBJS :=
+MOD_LIST_NAME := FC4_MODULES
+
+include ../../.config
+
+ifeq ($(CONFIG_FC4),y)
+ FC4 = fc.o
+ ifeq ($(CONFIG_MODULES),y)
+ O_TARGET := fc_n_syms.o
+ O_OBJS := fc.o
+ OX_OBJS := fc_syms.o
+ FC4 := $(O_TARGET)
+ endif
+ L_OBJS += $(FC4)
+else
+ ifeq ($(CONFIG_FC4),m)
+ MIX_OBJS += fc_syms.o
+ M_OBJS += fc4.o
+ endif
+endif
+
+ifeq ($(CONFIG_FC4_SOC),y)
+L_OBJS += soc.o
+else
+ ifeq ($(CONFIG_FC4_SOC),m)
+ M_OBJS += soc.o
+ endif
+endif
+
+include $(TOPDIR)/Rules.make
+
+fc4.o: $(MIX_OBJS) fc.o
+ $(LD) $(LD_RFLAG) -r -o $@ $(MIX_OBJS) fc.o
diff --git a/drivers/fc4/fc.c b/drivers/fc4/fc.c
new file mode 100644
index 000000000..04a18c0ff
--- /dev/null
+++ b/drivers/fc4/fc.c
@@ -0,0 +1,688 @@
+/* fc.c: Generic Fibre Channel and FC4 SCSI driver.
+ *
+ * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ * Copyright (C) 1997 Jiri Hanika (geo@ff.cuni.cz)
+ *
+ * Sources:
+ * Fibre Channel Physical & Signaling Interface (FC-PH), dpANS, 1994
+ * dpANS Fibre Channel Protocol for SCSI (X3.269-199X), Rev. 012, 1995
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/in.h>
+#include <linux/malloc.h>
+#include <linux/string.h>
+#include <linux/init.h>
+#include <linux/blk.h>
+
+#include <asm/pgtable.h>
+#include <asm/irq.h>
+#include <asm/semaphore.h>
+#include "fcp_scsi.h"
+#include "../scsi/hosts.h"
+
+/* #define FCDEBUG */
+
+#define fc_printk printk ("%s: ", fc->name); printk
+
+#ifdef FCDEBUG
+#define FCD(x) fc_printk x;
+#define FCND(x) printk ("FC: "); printk x;
+#else
+#define FCD(x)
+#define FCND(x)
+#endif
+
+#ifdef __sparc__
+static inline void *fc_dma_alloc(long size, char *name, dma_handle *dma)
+{
+ return (void *) sparc_dvma_malloc (size, "FCP SCSI cmd & rsp queues", dma);
+}
+
+static inline dma_handle fc_sync_dma_entry(void *buf, int len, fc_channel *fc)
+{
+ return mmu_get_scsi_one (buf, len, fc->dev->my_bus);
+}
+
+static inline void fc_sync_dma_exit(void *buf, long size, fc_channel *fc)
+{
+ mmu_release_scsi_one ((u32)(long)buf, size, fc->dev->my_bus);
+}
+
+static inline void fc_sync_dma_entry_sg(struct scatterlist *list, int count, fc_channel *fc)
+{
+ mmu_get_scsi_sgl((struct mmu_sglist *)list, count - 1, fc->dev->my_bus);
+}
+
+static inline void fc_sync_dma_exit_sg(struct scatterlist *list, int count, fc_channel *fc)
+{
+ mmu_release_scsi_sgl ((struct mmu_sglist *)list, count - 1, fc->dev->my_bus);
+}
+#else
+#error Port this
+#endif
+
+#define FCP_CMND(SCpnt) ((fcp_cmnd *)&(SCpnt->SCp))
+#define FC_SCMND(SCpnt) ((fc_channel *)(SCpnt->host->hostdata[0]))
+#define SC_FCMND(fcmnd) ((Scsi_Cmnd *)((long)fcmnd - (long)&(((Scsi_Cmnd *)0)->SCp)))
+
+static void fcp_scsi_insert_queue (fc_channel *fc, int no, fcp_cmnd *fcmd)
+{
+ if (!fc->scsi_que[no]) {
+ fc->scsi_que[no] = fcmd;
+ fcmd->next = fcmd;
+ fcmd->prev = fcmd;
+ } else {
+ fc->scsi_que[no]->prev->next = fcmd;
+ fcmd->prev = fc->scsi_que[no]->prev;
+ fc->scsi_que[no]->prev = fcmd;
+ fcmd->next = fc->scsi_que[no];
+ }
+}
+
+static void fcp_scsi_remove_queue (fc_channel *fc, int no, fcp_cmnd *fcmd)
+{
+ if (fcmd == fcmd->next) {
+ fc->scsi_que[no] = NULL;
+ return;
+ }
+ if (fcmd == fc->scsi_que[no])
+ fc->scsi_que[no] = fcmd->next;
+ fcmd->prev->next = fcmd->next;
+ fcmd->next->prev = fcmd->prev;
+}
+
+fc_channel *fc_channels = NULL;
+
+#define LSMAGIC 0x2a3b4d2a
+typedef struct {
+ /* Must be first */
+ struct semaphore sem;
+ int magic;
+ int count;
+ logi *logi;
+ fcp_cmnd *fcmds;
+ atomic_t todo;
+ struct timer_list timer;
+ int grace[1];
+} ls;
+
+#define LSOMAGIC 0x2a3c4e3c
+typedef struct {
+ /* Must be first */
+ struct semaphore sem;
+ int magic;
+ int count;
+ fcp_cmnd *fcmds;
+ atomic_t todo;
+ struct timer_list timer;
+} lso;
+
+static void fcp_login_timeout(unsigned long data)
+{
+ ls *l = (ls *)data;
+ FCND(("Login timeout\n"))
+ up(&l->sem);
+}
+
+static void fcp_login_done(fc_channel *fc, int i, int status)
+{
+ fcp_cmnd *fcmd;
+ logi *plogi;
+ fc_hdr *fch;
+ ls *l = (ls *)fc->ls;
+
+ FCD(("Login done %d %d\n", i, status))
+ if (i < l->count) {
+ if (fc->state == FC_STATE_FPORT_OK) {
+ FCD(("Additional FPORT_OK received with status %d\n", status))
+ return;
+ }
+ switch (status) {
+ case FC_STATUS_OK: /* Oh, we found a fabric */
+ case FC_STATUS_P_RJT: /* Oh, we haven't found any */
+ fc->state = FC_STATE_FPORT_OK;
+ fcmd = l->fcmds + i;
+ plogi = l->logi + 3 * i;
+ fc_sync_dma_exit (plogi, 3 * sizeof(logi), fc);
+ plogi->code = LS_PLOGI;
+ memcpy (&plogi->nport_wwn, &fc->wwn_nport, sizeof(fc_wwn));
+ memcpy (&plogi->node_wwn, &fc->wwn_node, sizeof(fc_wwn));
+ memcpy (&plogi->common, fc->common_svc, sizeof(common_svc_parm));
+ memcpy (&plogi->class1, fc->class_svcs, 3*sizeof(svc_parm));
+ fch = &fcmd->fch;
+ fcmd->token += l->count;
+ fcmd->rsp += sizeof(logi);
+ FILL_FCHDR_RCTL_DID(fch, R_CTL_ELS_REQ, fc->did);
+ FILL_FCHDR_SID(fch, fc->sid);
+#ifdef FCDEBUG
+ {
+ int i;
+ unsigned *x = (unsigned *)plogi;
+ printk ("logi: ");
+ for (i = 0; i < 21; i++)
+ printk ("%08x ", x[i]);
+ printk ("\n");
+ }
+#endif
+ fc_sync_dma_entry (plogi, 3 * sizeof(logi), fc);
+ if (fc->hw_enque (fc, fcmd))
+ printk ("FC: Cannot enque PLOGI packet on %s\n", fc->name);
+ break;
+ case FC_STATUS_ERR_OFFLINE:
+ fc->state = FC_STATE_MAYBEOFFLINE;
+ FCD (("FC is offline %d\n", l->grace[i]))
+ break;
+ default:
+ printk ("FLOGI failed for %s with status %d\n", fc->name, status);
+ /* Do some sort of error recovery here */
+ break;
+ }
+ } else {
+ i -= l->count;
+ if (fc->state != FC_STATE_FPORT_OK) {
+ FCD(("Unexpected N-PORT rsp received"))
+ return;
+ }
+ switch (status) {
+ case FC_STATUS_OK:
+ plogi = l->logi + 3 * i;
+ fc_sync_dma_exit (plogi, 3 * sizeof(logi), fc);
+ if (!fc->wwn_dest.lo && !fc->wwn_dest.hi) {
+ memcpy (&fc->wwn_dest, &plogi[1].node_wwn, sizeof(fc_wwn));
+ FCD(("Dest WWN %08x%08x\n", *(u32 *)&fc->wwn_dest, fc->wwn_dest.lo))
+ } else if (fc->wwn_dest.lo != plogi[1].node_wwn.lo ||
+ fc->wwn_dest.hi != plogi[1].node_wwn.hi) {
+ printk ("%s: mismatch in wwns. Got %08x%08x, expected %08x%08x\n",
+ fc->name,
+ *(u32 *)&plogi[1].node_wwn, plogi[1].node_wwn.lo,
+ *(u32 *)&fc->wwn_dest, fc->wwn_dest.lo);
+ }
+ fc->state = FC_STATE_ONLINE;
+ printk ("%s: ONLINE\n", fc->name);
+ if (atomic_dec_and_test (&l->todo))
+ up(&l->sem);
+ break;
+ case FC_STATUS_ERR_OFFLINE:
+ fc->state = FC_STATE_OFFLINE;
+ fc_sync_dma_exit (l->logi + 3 * i, 3 * sizeof(logi), fc);
+ printk ("%s: FC is offline\n", fc->name);
+ if (atomic_dec_and_test (&l->todo))
+ up(&l->sem);
+ break;
+ default:
+ printk ("PLOGI failed for %s with status %d\n", fc->name, status);
+ /* Do some sort of error recovery here */
+ break;
+ }
+ }
+}
+
+void fcp_register(fc_channel *fc, u8 type, int unregister)
+{
+ int size, i;
+
+ if (type == TYPE_SCSI_FCP) {
+ if (!unregister) {
+ fc->scsi_cmd_pool =
+ (fcp_cmd *) fc_dma_alloc (fc->can_queue * (sizeof (fcp_cmd) + fc->rsp_size),
+ "FCP SCSI cmd & rsp queues", &fc->dma_scsi_cmd);
+ fc->scsi_rsp_pool = (char *)(fc->scsi_cmd_pool + fc->can_queue);
+ fc->dma_scsi_rsp = fc->dma_scsi_cmd + fc->can_queue * sizeof (fcp_cmd);
+ fc->scsi_bitmap_end = ((fc->can_queue + 63) & ~63);
+ size = fc->scsi_bitmap_end / 8;
+ fc->scsi_bitmap = kmalloc (size, GFP_KERNEL);
+ memset (fc->scsi_bitmap, 0, size);
+ for (i = fc->can_queue; i < fc->scsi_bitmap_end; i++)
+ set_bit (i, fc->scsi_bitmap);
+ fc->scsi_free = fc->can_queue;
+ fc->token_tab = (fcp_cmnd **)kmalloc(fc->can_queue * sizeof(fcp_cmnd*), GFP_KERNEL);
+ } else {
+ fc->scsi_name[0] = 0;
+ kfree (fc->scsi_bitmap);
+ kfree (fc->token_tab);
+ }
+ } else
+ printk ("FC: %segistering unknown type %02x\n", unregister ? "Unr" : "R", type);
+}
+
+static void fcp_scsi_done(Scsi_Cmnd *SCpnt);
+
+static inline void fcp_scsi_receive(fc_channel *fc, int token, int status, fc_hdr *fch)
+{
+ fcp_cmnd *fcmd;
+ fcp_rsp *rsp;
+ int host_status;
+ Scsi_Cmnd *SCpnt;
+ int sense_len;
+ int rsp_status;
+
+ fcmd = fc->token_tab[token];
+ if (!fcmd) return;
+ rsp = (fcp_rsp *) (fc->scsi_rsp_pool + fc->rsp_size * token);
+ SCpnt = SC_FCMND(fcmd);
+
+ if (SCpnt->done != fcp_scsi_done)
+ return;
+
+ rsp_status = rsp->fcp_status;
+ switch (status) {
+ case FC_STATUS_OK:
+ host_status=DID_OK;
+
+ if (rsp_status & FCP_STATUS_RESID) {
+#ifdef FCDEBUG
+ FCD(("Resid %d\n", rsp->fcp_resid))
+ {
+ fcp_cmd *cmd = fc->scsi_cmd_pool + token;
+ int i;
+
+ printk ("Command ");
+ for (i = 0; i < sizeof(fcp_cmd); i+=4)
+ printk ("%08x ", *(u32 *)(((char *)cmd)+i));
+ printk ("\nResponse ");
+ for (i = 0; i < fc->rsp_size; i+=4)
+ printk ("%08x ", *(u32 *)(((char *)rsp)+i));
+ printk ("\n");
+ }
+#endif
+ }
+
+ if (rsp_status & FCP_STATUS_SENSE_LEN) {
+ sense_len = rsp->fcp_sense_len;
+ if (sense_len > sizeof(SCpnt->sense_buffer)) sense_len = sizeof(SCpnt->sense_buffer);
+ memcpy(SCpnt->sense_buffer, ((char *)(rsp+1)), sense_len);
+ }
+
+ if (fcmd->data) {
+ if (SCpnt->use_sg)
+ fc_sync_dma_exit_sg((struct scatterlist *)SCpnt->buffer, SCpnt->use_sg, fc);
+ else
+ fc_sync_dma_exit(SCpnt->request_buffer, SCpnt->request_bufflen, fc);
+ }
+ break;
+ default:
+ host_status=DID_ERROR;
+ FCD(("Wrong FC status %d for token %d\n", status, token))
+ break;
+ }
+
+ if (status_byte(rsp_status) == QUEUE_FULL) {
+ printk ("%s: (%d,%d) Received rsp_status 0x%x\n", fc->name, SCpnt->channel, SCpnt->target, rsp_status);
+ }
+
+ SCpnt->result = (host_status << 16) | (rsp_status & 0xff);
+ SCpnt->done = fcmd->done;
+ fcmd->done=NULL;
+ clear_bit(token, fc->scsi_bitmap);
+ fc->scsi_free++;
+ SCpnt->scsi_done(SCpnt);
+}
+
+void fcp_receive_solicited(fc_channel *fc, int proto, int token, int status, fc_hdr *fch)
+{
+ FCD(("Receive solicited %d %d %d\n", proto, token, status))
+ switch (proto) {
+ case TYPE_SCSI_FCP:
+ fcp_scsi_receive(fc, token, status, fch); break;
+ case TYPE_EXTENDED_LS:
+ if (fc->ls && ((ls *)(fc->ls))->magic == LSMAGIC) {
+ ls *l = (ls *)fc->ls;
+ int i = (token >= l->count) ? token - l->count : token;
+
+ /* Make us sure */
+ if ((unsigned)i < l->count && l->fcmds[i].fc == fc) {
+ fcp_login_done(fc, token, status);
+ break;
+ }
+ }
+ break;
+ case PROTO_OFFLINE:
+ if (fc->ls && ((lso *)(fc->ls))->magic == LSOMAGIC) {
+ lso *l = (lso *)fc->ls;
+
+ if ((unsigned)token < l->count && l->fcmds[token].fc == fc) {
+ /* Wow, OFFLINE response arrived :) */
+ FCD(("OFFLINE Response arrived\n"))
+ fc->state = FC_STATE_OFFLINE;
+ if (atomic_dec_and_test (&l->todo))
+ up(&l->sem);
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+void fcp_state_change(fc_channel *fc, int state)
+{
+ FCD(("state_change %d %d\n", state, fc->state))
+ if (state == FC_STATE_ONLINE && fc->state == FC_STATE_MAYBEOFFLINE)
+ fc->state = FC_STATE_UNINITED;
+ else if (state == FC_STATE_ONLINE)
+ printk (KERN_WARNING "%s: state change to ONLINE\n", fc->name);
+ else
+ printk (KERN_ERR "%s: state change to OFFLINE\n", fc->name);
+}
+
+int fcp_initialize(fc_channel *fcchain, int count)
+{
+ fc_channel *fc;
+ fcp_cmnd *fcmd;
+ int i, retry, ret;
+ ls *l;
+
+ FCND(("fcp_inititialize %08lx\n", (long)fcp_init))
+ FCND(("fc_channels %08lx\n", (long)fc_channels))
+ FCND((" SID %d DID %d\n", fcchain->sid, fcchain->did))
+ l = kmalloc(sizeof (ls) + count * sizeof(int), GFP_KERNEL);
+ if (!l) {
+ printk ("FC: Cannot allocate memory for initialization\n");
+ return -ENOMEM;
+ }
+ memset (l, 0, sizeof(ls) + count * sizeof(int));
+ l->magic = LSMAGIC;
+ l->count = count;
+ FCND(("FCP Init for %d channels\n", count))
+ l->sem = MUTEX_LOCKED;
+ l->timer.function = fcp_login_timeout;
+ l->timer.data = (unsigned long)l;
+ atomic_set (&l->todo, count);
+ l->logi = kmalloc (count * 3 * sizeof(logi), GFP_DMA);
+ l->fcmds = kmalloc (count * sizeof(fcp_cmnd), GFP_KERNEL);
+ if (!l->logi || !l->fcmds) {
+ if (l->logi) kfree (l->logi);
+ if (l->fcmds) kfree (l->fcmds);
+ kfree (l);
+ printk ("FC: Cannot allocate DMA memory for initialization\n");
+ return -ENOMEM;
+ }
+ memset (l->logi, 0, count * 3 * sizeof(logi));
+ memset (l->fcmds, 0, count * sizeof(fcp_cmnd));
+ FCND(("Initializing FLOGI packets\n"))
+ for (fc = fcchain, i = 0; fc && i < count; fc = fc->next, i++) {
+ fc_hdr *fch;
+
+ FCD(("SID %d DID %d\n", fc->sid, fc->did))
+ fc->state = FC_STATE_UNINITED;
+ fcmd = l->fcmds + i;
+ fc->login = fcmd;
+ fc->ls = (void *)l;
+ fch = &fcmd->fch;
+ FILL_FCHDR_RCTL_DID(fch, R_CTL_ELS_REQ, FS_FABRIC_F_PORT);
+ FILL_FCHDR_SID(fch, 0);
+ FILL_FCHDR_TYPE_FCTL(fch, TYPE_EXTENDED_LS, F_CTL_FIRST_SEQ | F_CTL_SEQ_INITIATIVE);
+ FILL_FCHDR_SEQ_DF_SEQ(fch, 0, 0, 0);
+ FILL_FCHDR_OXRX(fch, 0xffff, 0xffff);
+ fch->param = 0;
+ l->logi [3 * i].code = LS_FLOGI;
+ fcmd->cmd = fc_sync_dma_entry (l->logi + 3 * i, 3 * sizeof(logi), fc);
+ fcmd->rsp = fcmd->cmd + sizeof(logi);
+ fcmd->cmdlen = sizeof(logi);
+ fcmd->rsplen = sizeof(logi);
+ fcmd->data = (dma_handle)NULL;
+ fcmd->class = FC_CLASS_SIMPLE;
+ fcmd->proto = TYPE_EXTENDED_LS;
+ fcmd->token = i;
+ fcmd->fc = fc;
+ }
+ for (retry = 0; retry < 8; retry++) {
+ FCND(("Sending FLOGI/PLOGI packets\n"))
+ for (fc = fcchain, i = 0; fc && i < count; fc = fc->next, i++) {
+ if (fc->state == FC_STATE_ONLINE || fc->state == FC_STATE_OFFLINE)
+ continue;
+ disable_irq(fc->irq);
+ if (fc->state == FC_STATE_MAYBEOFFLINE) {
+ if (!l->grace[i]) {
+ l->grace[i]++;
+ FCD(("Grace\n"))
+ } else {
+ fc->state = FC_STATE_OFFLINE;
+ enable_irq(fc->irq);
+ fc_sync_dma_exit (l->logi + 3 * i, 3 * sizeof(logi), fc);
+ if (atomic_dec_and_test (&l->todo))
+ goto all_done;
+ }
+ }
+ ret = fc->hw_enque (fc, fc->login);
+ enable_irq(fc->irq);
+ if (ret) printk ("FC: Cannot enque FLOGI packet on %s\n", fc->name);
+ }
+
+ l->timer.expires = jiffies + 5 * HZ;
+ add_timer(&l->timer);
+
+ down(&l->sem);
+ if (!atomic_read(&l->todo)) {
+ FCND(("All channels answered in time\n"))
+ break; /* All fc channels have answered us */
+ }
+ }
+all_done:
+ for (fc = fcchain, i = 0; fc && i < count; fc = fc->next, i += 3) {
+ switch (fc->state) {
+ case FC_STATE_ONLINE: break;
+ case FC_STATE_OFFLINE: break;
+ default: fc_sync_dma_exit (l->logi + i, 3 * sizeof(logi), fc);
+ break;
+ }
+ fc->ls = NULL;
+ }
+ del_timer(&l->timer);
+ kfree (l->logi);
+ kfree (l->fcmds);
+ kfree (l);
+ return 0;
+}
+
+int fcp_forceoffline(fc_channel *fcchain, int count)
+{
+ fc_channel *fc;
+ fcp_cmnd *fcmd;
+ int i, ret;
+ lso l;
+
+ memset (&l, 0, sizeof(lso));
+ l.count = count;
+ l.magic = LSOMAGIC;
+ FCND(("FCP Force Offline for %d channels\n", count))
+ l.sem = MUTEX_LOCKED;
+ l.timer.function = fcp_login_timeout;
+ l.timer.data = (unsigned long)&l;
+ atomic_set (&l.todo, count);
+ l.fcmds = kmalloc (count * sizeof(fcp_cmnd), GFP_KERNEL);
+ if (!l.fcmds) {
+ kfree (l.fcmds);
+ printk ("FC: Cannot allocate memory for forcing offline\n");
+ return -ENOMEM;
+ }
+ memset (l.fcmds, 0, count * sizeof(fcp_cmnd));
+ FCND(("Initializing OFFLINE packets\n"))
+ for (fc = fcchain, i = 0; fc && i < count; fc = fc->next, i++) {
+ fc->state = FC_STATE_UNINITED;
+ fcmd = l.fcmds + i;
+ fc->login = fcmd;
+ fc->ls = (void *)&l;
+ fcmd->class = FC_CLASS_OFFLINE;
+ fcmd->proto = PROTO_OFFLINE;
+ fcmd->token = i;
+ fcmd->fc = fc;
+ disable_irq(fc->irq);
+ ret = fc->hw_enque (fc, fc->login);
+ enable_irq(fc->irq);
+ if (ret) printk ("FC: Cannot enque OFFLINE packet on %s\n", fc->name);
+ }
+
+ l.timer.expires = jiffies + 5 * HZ;
+ add_timer(&l.timer);
+ down(&l.sem);
+ del_timer(&l.timer);
+
+ kfree (l.fcmds);
+ return 0;
+}
+
+int fcp_init(fc_channel *fcchain)
+{
+ fc_channel *fc;
+ int count=0;
+
+ for (fc = fcchain; fc; fc = fc->next) {
+ fc->fcp_register = fcp_register;
+ fc->fcp_state_change = fcp_state_change;
+ count++;
+ }
+
+ fcp_initialize (fcchain, count);
+
+ if (!fc_channels)
+ fc_channels = fcchain;
+ else {
+ for (fc = fc_channels; fc->next; fc = fc->next);
+ fc->next = fcchain;
+ }
+ return 0;
+}
+
+static void fcp_scsi_done (Scsi_Cmnd *SCpnt)
+{
+ if (FCP_CMND(SCpnt)->done)
+ FCP_CMND(SCpnt)->done(SCpnt);
+}
+
+static int fcp_scsi_queue_it(fc_channel *fc, Scsi_Cmnd *SCpnt, fcp_cmnd *fcmd, int prepare)
+{
+ if (prepare) {
+ long i;
+ fcp_cmd *cmd;
+ u32 fcp_cntl;
+
+ i = find_first_zero_bit (fc->scsi_bitmap, fc->scsi_bitmap_end);
+ set_bit (i, fc->scsi_bitmap);
+ fcmd->token = i;
+ cmd = fc->scsi_cmd_pool + i;
+ FCD(("Chose token %ld %08lx\n", i, (long)cmd))
+ if (fc->encode_addr (SCpnt, cmd->fcp_addr)) {
+ /* Invalid channel/id/lun and couldn't map it into fcp_addr */
+ clear_bit (i, fc->scsi_bitmap);
+ SCpnt->result = (DID_BAD_TARGET << 16);
+ SCpnt->scsi_done(SCpnt);
+ return 0;
+ }
+ fc->scsi_free--;
+ fc->token_tab[fcmd->token] = fcmd;
+
+ if (SCpnt->device->tagged_supported) {
+ if (jiffies - fc->ages[SCpnt->channel * fc->targets + SCpnt->target] > (5 * 60 * HZ)) {
+ fc->ages[SCpnt->channel * fc->targets + SCpnt->target] = jiffies;
+ fcp_cntl = FCP_CNTL_QTYPE_ORDERED;
+ } else
+ fcp_cntl = FCP_CNTL_QTYPE_SIMPLE;
+ } else
+ fcp_cntl = FCP_CNTL_QTYPE_UNTAGGED;
+ if (!SCpnt->request_bufflen && !SCpnt->use_sg) {
+ cmd->fcp_cntl = fcp_cntl;
+ fcmd->data = (dma_handle)NULL;
+ } else {
+ switch (SCpnt->cmnd[0]) {
+ case WRITE_6:
+ case WRITE_10:
+ case WRITE_12:
+ cmd->fcp_cntl = (FCP_CNTL_WRITE | fcp_cntl); break;
+ default:
+ cmd->fcp_cntl = (FCP_CNTL_READ | fcp_cntl); break;
+ }
+ if (!SCpnt->use_sg) {
+ cmd->fcp_data_len = SCpnt->request_bufflen;
+ fcmd->data = fc_sync_dma_entry ((char *)SCpnt->request_buffer,
+ SCpnt->request_bufflen, fc);
+ } else {
+ struct scatterlist *sg = (struct scatterlist *)SCpnt->buffer;
+
+ FCD(("XXX: Use_sg %d %d\n", SCpnt->use_sg, sg->length))
+ if (SCpnt->use_sg > 1) printk ("%s: SG for use_sg > 1 not handled yet\n", fc->name);
+ fc_sync_dma_entry_sg (sg, SCpnt->use_sg, fc);
+ fcmd->data = sg->dvma_address;
+ cmd->fcp_data_len = sg->length;
+ }
+ }
+ memcpy (cmd->fcp_cdb, SCpnt->cmnd, SCpnt->cmd_len);
+ memset (cmd->fcp_cdb+SCpnt->cmd_len, 0, sizeof(cmd->fcp_cdb)-SCpnt->cmd_len);
+ FCD(("XXX: %04x.%04x.%04x.%04x - %08x%08x%08x\n", cmd->fcp_addr[0], cmd->fcp_addr[1], cmd->fcp_addr[2], cmd->fcp_addr[3], *(u32 *)SCpnt->cmnd, *(u32 *)(SCpnt->cmnd+4), *(u32 *)(SCpnt->cmnd+8)))
+ }
+ FCD(("Trying to enque %08x\n", (int)fcmd))
+ if (!fc->scsi_que[1]) {
+ if (!fc->hw_enque (fc, fcmd)) {
+ FCD(("hw_enque succeeded for %08x\n", (int)fcmd))
+ return 0;
+ }
+ }
+ FCD(("Putting into que1 %08x\n", (int)fcmd))
+ fcp_scsi_insert_queue (fc, 1, fcmd);
+ return 0;
+}
+
+int fcp_scsi_queuecommand(Scsi_Cmnd *SCpnt, void (* done)(Scsi_Cmnd *))
+{
+ fcp_cmnd *fcmd = FCP_CMND(SCpnt);
+ fc_channel *fc = FC_SCMND(SCpnt);
+
+ FCD(("Entering SCSI queuecommand %08x\n", (int)fcmd))
+ if (SCpnt->done != fcp_scsi_done) {
+ fcmd->done = SCpnt->done;
+ SCpnt->done = fcp_scsi_done;
+ SCpnt->scsi_done = done;
+ fcmd->proto = TYPE_SCSI_FCP;
+ if (!fc->scsi_free) {
+ FCD(("Putting into que0\n"))
+ fcp_scsi_insert_queue (fc, 0, fcmd);
+ return 1;
+ }
+ return fcp_scsi_queue_it(fc, SCpnt, fcmd, 1);
+ }
+ return fcp_scsi_queue_it(fc, SCpnt, fcmd, 0);
+}
+
+void fcp_queue_empty(fc_channel *fc)
+{
+ fcp_cmnd *fcmd;
+ FCD(("Queue empty\n"))
+ while ((fcmd = fc->scsi_que[1])) {
+ /* The hw tell us we can try again queue some packet */
+ if (fc->hw_enque (fc, fcmd))
+ return;
+ fcp_scsi_remove_queue (fc, 1, fcmd);
+ }
+}
+
+int fcp_scsi_abort(Scsi_Cmnd *SCpnt)
+{
+ printk ("FC: AIEEE, abort!\n");
+ return 0;
+}
+
+int fcp_scsi_reset(Scsi_Cmnd *SCpnt, unsigned int reset_flags)
+{
+ printk ("FC: AIEEE, reset!\n");
+ return 0;
+}
+
+#ifdef MODULE
+int init_module(void)
+{
+ return 0;
+}
+
+void cleanup_module(void)
+{
+}
+#endif
diff --git a/drivers/fc4/fc.h b/drivers/fc4/fc.h
new file mode 100644
index 000000000..d1c991802
--- /dev/null
+++ b/drivers/fc4/fc.h
@@ -0,0 +1,222 @@
+/* fc.h: Definitions for Fibre Channel Physical and Signaling Interface.
+ *
+ * Copyright (C) 1996,1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ *
+ * Sources:
+ * Fibre Channel Physical & Signaling Interface (FC-PH), dpANS, 1994
+ * dpANS Fibre Channel Protocol for SCSI (X3.269-199X), Rev. 012, 1995
+ */
+
+#ifndef __FC_H
+#define __FC_H
+
+/* World Wide Name */
+#define NAAID_IEEE 1
+#define NAAID_IEEE_EXT 2
+#define NAAID_LOCAL 3
+#define NAAID_IP 4
+#define NAAID_CCITT 12
+#define NAAID_CCITT_GRP 14
+
+typedef struct {
+ u32 naaid:4;
+ u32 nportid:12;
+ u32 hi:16;
+ u32 lo;
+} fc_wwn;
+
+/* Frame header for FC-PH frames */
+
+/* r_ctl field */
+#define R_CTL_DEVICE_DATA 0x00 /* FC4 Device_Data frame */
+#define R_CTL_EXTENDED_SVC 0x20 /* Extended Link_Data frame */
+#define R_CTL_FC4_SVC 0x30 /* FC4 Link_Data frame */
+#define R_CTL_VIDEO 0x40 /* Video_Data frame */
+#define R_CTL_BASIC_SVC 0x80 /* Basic Link_Data frame */
+#define R_CTL_LINK_CTL 0xc0 /* Link_Control frame */
+/* FC4 Device_Data frames */
+#define R_CTL_UNCATEGORIZED 0x00
+#define R_CTL_SOLICITED_DATA 0x01
+#define R_CTL_UNSOL_CONTROL 0x02
+#define R_CTL_SOLICITED_CONTROL 0x03
+#define R_CTL_UNSOL_DATA 0x04
+#define R_CTL_XFER_RDY 0x05
+#define R_CTL_COMMAND 0x06
+#define R_CTL_STATUS 0x07
+/* Basic Link_Data frames */
+#define R_CTL_LS_NOP 0x80
+#define R_CTL_LS_ABTS 0x81
+#define R_CTL_LS_RMC 0x82
+#define R_CTL_LS_BA_ACC 0x84
+#define R_CTL_LS_BA_RJT 0x85
+/* Extended Link_Data frames */
+#define R_CTL_ELS_REQ 0x22
+#define R_CTL_ELS_RSP 0x23
+/* Link_Control frames */
+#define R_CTL_ACK_1 0xc0
+#define R_CTL_ACK_N 0xc1
+#define R_CTL_P_RJT 0xc2
+#define R_CTL_F_RJT 0xc3
+#define R_CTL_P_BSY 0xc4
+#define R_CTL_F_BSY_DF 0xc5
+#define R_CTL_F_BSY_LC 0xc6
+#define R_CTL_LCR 0xc7
+
+/* type field */
+#define TYPE_BASIC_LS 0x00
+#define TYPE_EXTENDED_LS 0x01
+#define TYPE_IS8802 0x04
+#define TYPE_IS8802_SNAP 0x05
+#define TYPE_SCSI_FCP 0x08
+#define TYPE_SCSI_GPP 0x09
+#define TYPE_HIPP_FP 0x0a
+#define TYPE_IPI3_MASTER 0x11
+#define TYPE_IPI3_SLAVE 0x12
+#define TYPE_IPI3_PEER 0x13
+
+/* f_ctl field */
+#define F_CTL_FILL_BYTES 0x000003
+#define F_CTL_XCHG_REASSEMBLE 0x000004
+#define F_CTL_RO_PRESENT 0x000008
+#define F_CTL_ABORT_SEQ 0x000030
+#define F_CTL_CONTINUE_SEQ 0x0000c0
+#define F_CTL_INVALIDATE_XID 0x004000
+#define F_CTL_XID_REASSIGNED 0x008000
+#define F_CTL_SEQ_INITIATIVE 0x010000
+#define F_CTL_CHAINED_SEQ 0x020000
+#define F_CTL_END_CONNECT 0x040000
+#define F_CTL_END_SEQ 0x080000
+#define F_CTL_LAST_SEQ 0x100000
+#define F_CTL_FIRST_SEQ 0x200000
+#define F_CTL_SEQ_CONTEXT 0x400000
+#define F_CTL_XCHG_CONTEXT 0x800000
+
+typedef struct {
+ u32 r_ctl:8, did:24;
+ u32 xxx1:8, sid:24;
+ u32 type:8, f_ctl:24;
+ u32 seq_id:8, df_ctl:8, seq_cnt:16;
+ u16 ox_id, rx_id;
+ u32 param;
+} fc_hdr;
+/* The following are ugly macros to make setup of this structure faster */
+#define FILL_FCHDR_RCTL_DID(fch, r_ctl, did) *(u32 *)(fch) = ((r_ctl) << 24) | (did);
+#define FILL_FCHDR_SID(fch, sid) *((u32 *)(fch)+1) = (sid);
+#define FILL_FCHDR_TYPE_FCTL(fch, type, f_ctl) *((u32 *)(fch)+2) = ((type) << 24) | (f_ctl);
+#define FILL_FCHDR_SEQ_DF_SEQ(fch, seq_id, df_ctl, seq_cnt) *((u32 *)(fch)+3) = ((seq_id) << 24) | ((df_ctl) << 16) | (seq_cnt);
+#define FILL_FCHDR_OXRX(fch, ox_id, rx_id) *((u32 *)(fch)+4) = ((ox_id) << 16) | (rx_id);
+
+/* Well known addresses */
+#define FS_GENERAL_MULTICAST 0xfffff7
+#define FS_WELL_KNOWN_MULTICAST 0xfffff8
+#define FS_HUNT_GROUP 0xfffff9
+#define FS_MANAGEMENT_SERVER 0xfffffa
+#define FS_TIME_SERVER 0xfffffb
+#define FS_NAME_SERVER 0xfffffc
+#define FS_FABRIC_CONTROLLER 0xfffffd
+#define FS_FABRIC_F_PORT 0xfffffe
+#define FS_BROADCAST 0xffffff
+
+/* Reject frames */
+/* The param field should be cast to this structure */
+typedef struct {
+ u8 action;
+ u8 reason;
+ u8 xxx;
+ u8 vendor_unique;
+} rjt_param;
+
+/* Reject action codes */
+#define RJT_RETRY 0x01
+#define RJT_NONRETRY 0x02
+
+/* Reject reason codes */
+#define RJT_INVALID_DID 0x01
+#define RJT_INVALID_SID 0x02
+#define RJT_NPORT_NOT_AVAIL_TEMP 0x03
+#define RJT_NPORT_NOT_AVAIL_PERM 0x04
+#define RJT_CLASS_NOT_SUPPORTED 0x05
+#define RJT_DELIMITER_ERROR 0x06
+#define RJT_TYPE_NOT_SUPPORTED 0x07
+#define RJT_INVALID_LINK_CONTROL 0x08
+#define RJT_INVALID_R_CTL 0x09
+#define RJT_INVALID_F_CTL 0x0a
+#define RJT_INVALID_OX_ID 0x0b
+#define RJT_INVALID_RX_ID 0x0c
+#define RJT_INVALID_SEQ_ID 0x0d
+#define RJT_INVALID_DF_CTL 0x0e
+#define RJT_INVALID_SEQ_CNT 0x0f
+#define RJT_INVALID_PARAMETER 0x10
+#define RJT_EXCHANGE_ERROR 0x11
+#define RJT_PROTOCOL_ERROR 0x12
+#define RJT_INCORRECT_LENGTH 0x13
+#define RJT_UNEXPECTED_ACK 0x14
+#define RJT_UNEXPECTED_LINK_RESP 0x15
+#define RJT_LOGIN_REQUIRED 0x16
+#define RJT_EXCESSIVE_SEQUENCES 0x17
+#define RJT_CANT_ESTABLISH_EXCHANGE 0x18
+#define RJT_SECURITY_NOT_SUPPORTED 0x19
+#define RJT_FABRIC_NA 0x1a
+#define RJT_VENDOR_UNIQUE 0xff
+
+
+#define SP_F_PORT_LOGIN 0x10
+
+/* Extended SVC commands */
+#define LS_RJT 0x01000000
+#define LS_ACC 0x02000000
+#define LS_PLOGI 0x03000000
+#define LS_FLOGI 0x04000000
+#define LS_LOGO 0x05000000
+#define LS_ABTX 0x06000000
+#define LS_RCS 0x07000000
+#define LS_RES 0x08000000
+#define LS_RSS 0x09000000
+#define LS_RSI 0x0a000000
+#define LS_ESTS 0x0b000000
+#define LS_ESTC 0x0c000000
+#define LS_ADVC 0x0d000000
+#define LS_RTV 0x0e000000
+#define LS_RLS 0x0f000000
+#define LS_ECHO 0x10000000
+#define LS_TEST 0x11000000
+#define LS_RRQ 0x12000000
+#define LS_IDENT 0x20000000
+#define LS_DISPLAY 0x21000000
+
+typedef struct {
+ u8 fcph_hi, fcph_lo;
+ u16 buf2buf_credit;
+ u8 common_features;
+ u8 xxx1;
+ u16 buf2buf_size;
+ u8 xxx2;
+ u8 total_concurrent;
+ u16 off_by_info;
+ u32 e_d_tov;
+} common_svc_parm;
+
+typedef struct {
+ u16 serv_opts;
+ u16 initiator_ctl;
+ u16 rcpt_ctl;
+ u16 recv_size;
+ u8 xxx1;
+ u8 concurrent_seqs;
+ u16 end2end_credit;
+ u16 open_seqs_per_xchg;
+ u16 xxx2;
+} svc_parm;
+
+/* Login */
+typedef struct {
+ u32 code;
+ common_svc_parm common;
+ fc_wwn nport_wwn;
+ fc_wwn node_wwn;
+ svc_parm class1;
+ svc_parm class2;
+ svc_parm class3;
+} logi;
+
+#endif /* !(__FC_H) */
diff --git a/drivers/fc4/fc_syms.c b/drivers/fc4/fc_syms.c
new file mode 100644
index 000000000..f896a9b0f
--- /dev/null
+++ b/drivers/fc4/fc_syms.c
@@ -0,0 +1,28 @@
+/*
+ * We should not even be trying to compile this if we are not doing
+ * a module.
+ */
+#define __NO_VERSION__
+#include <linux/config.h>
+#include <linux/module.h>
+
+#ifdef CONFIG_MODULES
+
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+
+#include "fcp_scsi.h"
+
+EXPORT_SYMBOL(fcp_init);
+EXPORT_SYMBOL(fcp_queue_empty);
+EXPORT_SYMBOL(fcp_receive_solicited);
+EXPORT_SYMBOL(fc_channels);
+
+/* SCSI stuff */
+EXPORT_SYMBOL(fcp_scsi_queuecommand);
+EXPORT_SYMBOL(fcp_scsi_abort);
+EXPORT_SYMBOL(fcp_scsi_reset);
+
+#endif /* CONFIG_MODULES */
diff --git a/drivers/fc4/fcp.h b/drivers/fc4/fcp.h
new file mode 100644
index 000000000..6aa34a7a4
--- /dev/null
+++ b/drivers/fc4/fcp.h
@@ -0,0 +1,94 @@
+/* fcp.h: Definitions for Fibre Channel Protocol.
+ *
+ * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ *
+ */
+
+#ifndef __FCP_H
+#define __FCP_H
+
+/* FCP addressing is hierarchical with up to 4 layers, MS first.
+ Exact meaning of the addresses is up to the vendor */
+
+/* fcp_cntl field */
+#define FCP_CNTL_WRITE 0x00000001 /* Initiator write */
+#define FCP_CNTL_READ 0x00000002 /* Initiator read */
+#define FCP_CNTL_ABORT_TSK 0x00000200 /* Abort task set */
+#define FCP_CNTL_CLR_TASK 0x00000400 /* Clear task set */
+#define FCP_CNTL_RESET 0x00002000 /* Reset */
+#define FCP_CNTL_CLR_ACA 0x00004000 /* Clear ACA */
+#define FCP_CNTL_KILL_TASK 0x00008000 /* Terminate task */
+#define FCP_CNTL_QTYPE_MASK 0x00070000 /* Tagged queueing type */
+#define FCP_CNTL_QTYPE_SIMPLE 0x00000000
+#define FCP_CNTL_QTYPE_HEAD_OF_Q 0x00010000
+#define FCP_CNTL_QTYPE_ORDERED 0x00020000
+#define FCP_CNTL_QTYPE_ACA_Q_TAG 0x00040000
+#define FCP_CNTL_QTYPE_UNTAGGED 0x00050000
+
+typedef struct {
+ u16 fcp_addr[4];
+ u32 fcp_cntl;
+ u8 fcp_cdb[16];
+ u32 fcp_data_len;
+} fcp_cmd;
+
+/* fcp_status field */
+#define FCP_STATUS_MASK 0x000000ff /* scsi status of command */
+#define FCP_STATUS_RSP_LEN 0x00000100 /* response_len != 0 */
+#define FCP_STATUS_SENSE_LEN 0x00000200 /* sense_len != 0 */
+#define FCP_STATUS_RESID 0x00000400 /* resid != 0 */
+
+typedef struct {
+ u32 xxx[2];
+ u32 fcp_status;
+ u32 fcp_resid;
+ u32 fcp_sense_len;
+ u32 fcp_response_len;
+ /* u8 fcp_sense[fcp_sense_len]; */
+ /* u8 fcp_response[fcp_response_len]; */
+} fcp_rsp;
+
+/* fcp errors */
+
+/* rsp_info_type field */
+#define FCP_RSP_SCSI_BUS_ERR 0x01
+#define FCP_RSP_SCSI_PORT_ERR 0x02
+#define FCP_RSP_CARD_ERR 0x03
+
+/* isp_status field */
+#define FCP_RSP_CMD_COMPLETE 0x0000
+#define FCP_RSP_CMD_INCOMPLETE 0x0001
+#define FCP_RSP_CMD_DMA_ERR 0x0002
+#define FCP_RSP_CMD_TRAN_ERR 0x0003
+#define FCP_RSP_CMD_RESET 0x0004
+#define FCP_RSP_CMD_ABORTED 0x0005
+#define FCP_RSP_CMD_TIMEOUT 0x0006
+#define FCP_RSP_CMD_OVERRUN 0x0007
+
+/* isp_state_flags field */
+#define FCP_RSP_ST_GOT_BUS 0x0100
+#define FCP_RSP_ST_GOT_TARGET 0x0200
+#define FCP_RSP_ST_SENT_CMD 0x0400
+#define FCP_RSP_ST_XFRD_DATA 0x0800
+#define FCP_RSP_ST_GOT_STATUS 0x1000
+#define FCP_RSP_ST_GOT_SENSE 0x2000
+
+/* isp_stat_flags field */
+#define FCP_RSP_STAT_DISC 0x0001
+#define FCP_RSP_STAT_SYNC 0x0002
+#define FCP_RSP_STAT_PERR 0x0004
+#define FCP_RSP_STAT_BUS_RESET 0x0008
+#define FCP_RSP_STAT_DEV_RESET 0x0010
+#define FCP_RSP_STAT_ABORTED 0x0020
+#define FCP_RSP_STAT_TIMEOUT 0x0040
+#define FCP_RSP_STAT_NEGOTIATE 0x0080
+
+typedef struct {
+ u8 rsp_info_type;
+ u8 xxx;
+ u16 isp_status;
+ u16 isp_state_flags;
+ u16 isp_stat_flags;
+} fcp_scsi_err;
+
+#endif /* !(__FCP_H) */
diff --git a/drivers/fc4/fcp_scsi.h b/drivers/fc4/fcp_scsi.h
new file mode 100644
index 000000000..6f8cc0257
--- /dev/null
+++ b/drivers/fc4/fcp_scsi.h
@@ -0,0 +1,140 @@
+/* fcp_scsi.h: Generic SCSI on top of FC4 - interface defines.
+ *
+ * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ */
+
+#ifndef _FCP_SCSI_H
+#define _FCP_SCSI_H
+
+#include <linux/types.h>
+#include <linux/blk.h>
+#include "../scsi/scsi.h"
+
+#include "fc.h"
+#include "fcp.h"
+
+#ifdef __sparc__
+
+#include <asm/sbus.h>
+typedef u32 dma_handle;
+
+#else
+#error Need to port FC layer to your architecture
+#endif
+
+#define FC_CLASS_OUTBOUND 0x01
+#define FC_CLASS_INBOUND 0x02
+#define FC_CLASS_SIMPLE 0x03
+#define FC_CLASS_IO_WRITE 0x04
+#define FC_CLASS_IO_READ 0x05
+#define FC_CLASS_UNSOLICITED 0x06
+#define FC_CLASS_OFFLINE 0x08
+
+#define PROTO_OFFLINE 0x02
+
+struct _fc_channel;
+
+typedef struct fcp_cmnd {
+ struct fcp_cmnd *next;
+ struct fcp_cmnd *prev;
+ void (*done)(Scsi_Cmnd *);
+ int proto;
+ int token;
+ /* FCP SCSI stuff */
+ dma_handle data;
+ /* From now on this cannot be touched for proto == TYPE_SCSI_FCP */
+ fc_hdr fch;
+ dma_handle cmd;
+ dma_handle rsp;
+ int cmdlen;
+ int rsplen;
+ int class;
+ int datalen;
+ /* This is just used as a verification during login */
+ struct _fc_channel *fc;
+} fcp_cmnd;
+
+typedef struct _fc_channel {
+ struct _fc_channel *next;
+ int irq;
+ int state;
+ int sid;
+ int did;
+ char name[16];
+ void (*fcp_register)(struct _fc_channel *, u8, int);
+ void (*fcp_state_change)(struct _fc_channel *, int);
+ void (*reset)(struct _fc_channel *);
+ int (*hw_enque)(struct _fc_channel *, fcp_cmnd *);
+ fc_wwn wwn_node;
+ fc_wwn wwn_nport;
+ fc_wwn wwn_dest;
+ common_svc_parm *common_svc;
+ svc_parm *class_svcs;
+#ifdef __sparc__
+ struct linux_sbus_device *dev;
+#endif
+ /* FCP SCSI stuff */
+ int can_queue;
+ int rsp_size;
+ fcp_cmd *scsi_cmd_pool;
+ char *scsi_rsp_pool;
+ dma_handle dma_scsi_cmd, dma_scsi_rsp;
+ long *scsi_bitmap;
+ long scsi_bitmap_end;
+ int scsi_free;
+ int (*encode_addr)(Scsi_Cmnd *cmnd, u16 *addr);
+ fcp_cmnd *scsi_que[2];
+ char scsi_name[4];
+ fcp_cmnd **token_tab;
+ int channels;
+ int targets;
+ long *ages;
+ /* LOGIN stuff */
+ fcp_cmnd *login;
+ void *ls;
+} fc_channel;
+
+extern fc_channel *fc_channels;
+
+#define FC_STATE_UNINITED 0
+#define FC_STATE_ONLINE 1
+#define FC_STATE_OFFLINE 2
+#define FC_STATE_RESETING 3
+#define FC_STATE_FPORT_OK 4
+#define FC_STATE_MAYBEOFFLINE 5
+
+#define FC_STATUS_OK 0
+#define FC_STATUS_P_RJT 2
+#define FC_STATUS_F_RJT 3
+#define FC_STATUS_P_BSY 4
+#define FC_STATUS_F_BSY 5
+#define FC_STATUS_ERR_OFFLINE 0x11
+#define FC_STATUS_TIMEOUT 0x12
+#define FC_STATUS_ERR_OVERRUN 0x13
+#define FC_STATUS_UNKNOWN_CQ_TYPE 0x20
+#define FC_STATUS_BAD_SEG_CNT 0x21
+#define FC_STATUS_MAX_XCHG_EXCEEDED 0x22
+#define FC_STATUS_BAD_XID 0x23
+#define FC_STATUS_XCHG_BUSY 0x24
+#define FC_STATUS_BAD_POOL_ID 0x25
+#define FC_STATUS_INSUFFICIENT_CQES 0x26
+#define FC_STATUS_ALLOC_FAIL 0x27
+#define FC_STATUS_BAD_SID 0x28
+#define FC_STATUS_NO_SEQ_INIT 0x29
+
+void fcp_queue_empty(fc_channel *);
+int fcp_init(fc_channel *);
+void fcp_receive_solicited(fc_channel *, int, int, int, fc_hdr *);
+
+#define for_each_fc_channel(fc) \
+ for (fc = fc_channels; fc; fc = fc->next)
+
+#define for_each_online_fc_channel(fc) \
+ for_each_fc_channel(fc) \
+ if (fc->state == FC_STATE_ONLINE)
+
+int fcp_scsi_queuecommand(Scsi_Cmnd *, void (* done)(Scsi_Cmnd *));
+int fcp_scsi_abort(Scsi_Cmnd *);
+int fcp_scsi_reset(Scsi_Cmnd *, unsigned int);
+
+#endif /* !(_FCP_SCSI_H) */
diff --git a/drivers/fc4/soc.c b/drivers/fc4/soc.c
new file mode 100644
index 000000000..ee3f513e1
--- /dev/null
+++ b/drivers/fc4/soc.c
@@ -0,0 +1,710 @@
+/* soc.c: Sparc SUNW,soc (Serial Optical Channel) Fibre Channel Sbus adapter support.
+ *
+ * Copyright (C) 1996,1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ * Copyright (C) 1997 Jiri Hanika (geo@ff.cuni.cz)
+ *
+ * Sources:
+ * Fibre Channel Physical & Signaling Interface (FC-PH), dpANS, 1994
+ * dpANS Fibre Channel Protocol for SCSI (X3.269-199X), Rev. 012, 1995
+ */
+
+static char *version =
+ "soc.c:v1.0 24/Nov/97 Jakub Jelinek (jj@sunsite.mff.cuni.cz), Jiri Hanika (geo@ff.cuni.cz)\n";
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/in.h>
+#include <linux/malloc.h>
+#include <linux/string.h>
+#include <linux/init.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <linux/errno.h>
+#include <asm/byteorder.h>
+
+#include <asm/openprom.h>
+#include <asm/oplib.h>
+#include <asm/auxio.h>
+#include <asm/system.h>
+#include <asm/pgtable.h>
+#include <asm/irq.h>
+
+/* #define SOCDEBUG */
+/* #define HAVE_SOC_UCODE */
+
+#include "fcp_scsi.h"
+#include "soc.h"
+#ifdef HAVE_SOC_UCODE
+#include "soc_asm.c"
+#endif
+
+#define soc_printk printk ("soc%d: ", s->soc_no); printk
+
+#ifdef SOCDEBUG
+#define SOD(x) soc_printk x;
+#else
+#define SOD(x)
+#endif
+
+#define for_each_soc(s) for (s = socs; s; s = s->next)
+struct soc *socs = NULL;
+
+static inline void soc_disable(struct soc *s)
+{
+ s->regs->imask = 0; s->regs->cmd = SOC_CMD_SOFT_RESET;
+}
+
+static inline void soc_enable(struct soc *s)
+{
+ SOD(("enable %08x\n", s->cfg))
+ s->regs->sae = 0; s->regs->cfg = s->cfg;
+ s->regs->cmd = SOC_CMD_RSP_QALL;
+ SOC_SETIMASK(s, SOC_IMASK_RSP_QALL | SOC_IMASK_SAE);
+}
+
+static void soc_reset(fc_channel *fc)
+{
+ soc_port *port = (soc_port *)fc;
+ struct soc *s = port->s;
+
+ /* FIXME */
+ soc_disable(s);
+ s->req[0].seqno = 1;
+ s->req[1].seqno = 1;
+ s->rsp[0].seqno = 1;
+ s->rsp[1].seqno = 1;
+ s->req[0].in = 0;
+ s->req[1].in = 0;
+ s->rsp[0].in = 0;
+ s->rsp[1].in = 0;
+ s->req[0].out = 0;
+ s->req[1].out = 0;
+ s->rsp[0].out = 0;
+ s->rsp[1].out = 0;
+
+ /* FIXME */
+ soc_enable(s);
+}
+
+static void inline soc_solicited (struct soc *s)
+{
+ fc_hdr fchdr;
+ soc_rsp *hwrsp;
+ soc_cq *sw_cq;
+ int token;
+ int status;
+ fc_channel *fc;
+
+ sw_cq = &s->rsp[SOC_SOLICITED_RSP_Q];
+
+ if (sw_cq->pool == NULL)
+ sw_cq->pool =
+ (soc_req *)(s->xram + (xram_get_32low ((xram_p)&sw_cq->hw_cq->address) / sizeof(u16)));
+ sw_cq->in = xram_get_8 ((xram_p)&sw_cq->hw_cq->in);
+ SOD (("soc_solicited, %d packets arrived\n", (sw_cq->in - sw_cq->out) & sw_cq->last))
+ for (;;) {
+ hwrsp = (soc_rsp *)sw_cq->pool + sw_cq->out;
+ token = xram_get_32low ((xram_p)&hwrsp->shdr.token);
+ status = xram_get_32low ((xram_p)&hwrsp->status);
+ fc = (fc_channel *)(&s->port[(token >> 11) & 1]);
+
+ if (status == SOC_OK)
+ fcp_receive_solicited(fc, token >> 12, token & ((1 << 11) - 1), FC_STATUS_OK, NULL);
+ else {
+ xram_copy_from(&fchdr, (xram_p)&hwrsp->fchdr, sizeof(fchdr));
+ /* We have intentionally defined FC_STATUS_* constants to match SOC_* constants, otherwise
+ we'd have to translate status */
+ fcp_receive_solicited(fc, token >> 12, token & ((1 << 11) - 1), status, &fchdr);
+ }
+
+ if (++sw_cq->out > sw_cq->last) {
+ sw_cq->seqno++;
+ sw_cq->out = 0;
+ }
+
+ if (sw_cq->out == sw_cq->in) {
+ sw_cq->in = xram_get_8 ((xram_p)&sw_cq->hw_cq->in);
+ if (sw_cq->out == sw_cq->in) {
+ /* Tell the hardware about it */
+ s->regs->cmd = (sw_cq->out << 24) | (SOC_CMD_RSP_QALL & ~(SOC_CMD_RSP_Q0 << SOC_SOLICITED_RSP_Q));
+ /* Read it, so that we're sure it has been updated */
+ s->regs->cmd;
+ sw_cq->in = xram_get_8 ((xram_p)&sw_cq->hw_cq->in);
+ if (sw_cq->out == sw_cq->in)
+ break;
+ }
+ }
+ }
+}
+
+static void inline soc_request (struct soc *s, u32 cmd)
+{
+ SOC_SETIMASK(s, s->imask & ~(cmd & SOC_CMD_REQ_QALL));
+
+ SOD(("Queues available %08x OUT %X %X\n", cmd, xram_get_8((xram_p)&s->req[0].hw_cq->out), xram_get_8((xram_p)&s->req[0].hw_cq->out)))
+ if (s->port[s->curr_port].fc.state != FC_STATE_OFFLINE) {
+ fcp_queue_empty ((fc_channel *)&(s->port[s->curr_port]));
+ if (((s->req[1].in + 1) & s->req[1].last) != (s->req[1].out))
+ fcp_queue_empty ((fc_channel *)&(s->port[1 - s->curr_port]));
+ } else
+ fcp_queue_empty ((fc_channel *)&(s->port[1 - s->curr_port]));
+ if (s->port[1 - s->curr_port].fc.state != FC_STATE_OFFLINE)
+ s->curr_port ^= 1;
+}
+
+static void inline soc_unsolicited (struct soc *s)
+{
+ soc_rsp *hwrsp, *hwrspc;
+ soc_cq *sw_cq;
+ int count;
+ int status;
+ int flags;
+ fc_channel *fc;
+
+ sw_cq = &s->rsp[SOC_UNSOLICITED_RSP_Q];
+ if (sw_cq->pool == NULL)
+ sw_cq->pool =
+ (soc_req *)(s->xram + (xram_get_32low ((xram_p)&sw_cq->hw_cq->address) / sizeof(u16)));
+
+ sw_cq->in = xram_get_8 ((xram_p)&sw_cq->hw_cq->in);
+ SOD (("soc_unsolicited, %d packets arrived\n", (sw_cq->in - sw_cq->out) & sw_cq->last))
+ while (sw_cq->in != sw_cq->out) {
+ /* ...real work per entry here... */
+ hwrsp = (soc_rsp *)sw_cq->pool + sw_cq->out;
+ hwrspc = NULL;
+ flags = hwrsp->shdr.flags;
+ count = xram_get_8 ((xram_p)&hwrsp->count);
+ fc = (fc_channel *)&s->port[flags & SOC_PORT_B];
+ SOD(("FC %08lx fcp_state_change %08lx\n", (long)fc, (long)fc->fcp_state_change))
+
+ if (count != 1) {
+ /* Ugh, continuation entries */
+ u8 in;
+
+ if (count != 2) {
+ printk("%s: Too many continuations entries %d\n", fc->name, count);
+ goto update_out;
+ }
+
+ in = sw_cq->in;
+ if (in < sw_cq->out) in += sw_cq->last + 1;
+ if (in < sw_cq->out + 2) {
+ /* Ask the hardware about it if they haven't arrived yet */
+ s->regs->cmd = (sw_cq->out << 24) | (SOC_CMD_RSP_QALL & ~(SOC_CMD_RSP_Q0 << SOC_UNSOLICITED_RSP_Q));
+ /* Read it, so that we're sure it has been updated */
+ s->regs->cmd;
+ sw_cq->in = xram_get_8 ((xram_p)&sw_cq->hw_cq->in);
+ in = sw_cq->in;
+ if (in < sw_cq->out) in += sw_cq->last + 1;
+ if (in < sw_cq->out + 2) /* Nothing came, let us wait */
+ return;
+ }
+ if (sw_cq->out == sw_cq->last)
+ hwrspc = (soc_rsp *)sw_cq->pool;
+ else
+ hwrspc = hwrsp + 1;
+ }
+
+ switch (flags & ~SOC_PORT_B) {
+ case SOC_STATUS:
+ status = xram_get_32low ((xram_p)&hwrsp->status);
+ switch (status) {
+ case SOC_ONLINE:
+ fc->fcp_state_change(fc, FC_STATE_ONLINE);
+ break;
+ case SOC_OFFLINE:
+ fc->fcp_state_change(fc, FC_STATE_OFFLINE);
+ break;
+ default:
+ printk ("%s: Unknown STATUS no %d\n", fc->name, status);
+ break;
+ }
+ break;
+ case (SOC_UNSOLICITED|SOC_FC_HDR):
+ {
+ int r_ctl = xram_get_8 ((xram_p)&hwrsp->fchdr);
+ unsigned len;
+ char buf[64];
+
+ if ((r_ctl & 0xf0) == R_CTL_EXTENDED_SVC) {
+ len = xram_get_32 ((xram_p)&hwrsp->shdr.bytecnt);
+ if (len < 4 || !hwrspc)
+ printk ("%s: Invalid R_CTL %02x continuation entries\n", fc->name, r_ctl);
+ else {
+ if (len > 60) len = 60;
+ xram_copy_from (buf, (xram_p)hwrspc, (len + 3) & ~3);
+ if (*(u32 *)buf == LS_DISPLAY) {
+ int i;
+
+ for (i = 4; i < len; i++)
+ if (buf[i] == '\n') buf[i] = ' ';
+ buf[len] = 0;
+ printk ("%s message: %s\n", fc->name, buf + 4);
+ } else
+ printk ("%s: Unknown LS_CMD %02x\n", fc->name, buf[0]);
+ }
+ } else
+ printk ("%s: Unsolicited R_CTL %02x not handled\n", fc->name, r_ctl);
+ }
+ break;
+ default:
+ printk ("%s: Unexpected flags %08x\n", fc->name, flags);
+ break;
+ }
+update_out:
+ if (++sw_cq->out > sw_cq->last) {
+ sw_cq->seqno++;
+ sw_cq->out = 0;
+ }
+
+ if (hwrspc) {
+ if (++sw_cq->out > sw_cq->last) {
+ sw_cq->seqno++;
+ sw_cq->out = 0;
+ }
+ }
+
+ if (sw_cq->out == sw_cq->in) {
+ sw_cq->in = xram_get_8 ((xram_p)&sw_cq->hw_cq->in);
+ if (sw_cq->out == sw_cq->in) {
+ /* Tell the hardware about it */
+ s->regs->cmd = (sw_cq->out << 24) | (SOC_CMD_RSP_QALL & ~(SOC_CMD_RSP_Q0 << SOC_UNSOLICITED_RSP_Q));
+ /* Read it, so that we're sure it has been updated */
+ s->regs->cmd;
+ sw_cq->in = xram_get_8 ((xram_p)&sw_cq->hw_cq->in);
+ }
+ }
+ }
+}
+
+static void soc_intr(int irq, void *dev_id, struct pt_regs *regs)
+{
+ u32 cmd;
+ register struct soc *s = (struct soc *)dev_id;
+
+ cmd = s->regs->cmd;
+ for (; (cmd = SOC_INTR (s, cmd)); cmd = s->regs->cmd) {
+ if (cmd & SOC_CMD_RSP_Q1) soc_unsolicited (s);
+ if (cmd & SOC_CMD_RSP_Q0) soc_solicited (s);
+ if (cmd & SOC_CMD_REQ_QALL) soc_request (s, cmd);
+ }
+}
+
+#define TOKEN(proto, port, token) (((proto)<<12)|(token)|(port))
+
+static int soc_hw_enque (fc_channel *fc, fcp_cmnd *fcmd)
+{
+ soc_port *port = (soc_port *)fc;
+ struct soc *s = port->s;
+ int qno;
+ soc_cq *sw_cq;
+ int cq_next_in;
+ soc_req *request;
+ fc_hdr *fch;
+ int i;
+
+ if (fcmd->proto == TYPE_SCSI_FCP)
+ qno = 1;
+ else
+ qno = 0;
+ SOD(("Putting a FCP packet type %d into hw queue %d\n", fcmd->proto, qno))
+ if (s->imask & (SOC_IMASK_REQ_Q0 << qno))
+ return -EIO;
+ sw_cq = s->req + qno;
+ cq_next_in = (sw_cq->in + 1) & sw_cq->last;
+
+ if (cq_next_in == sw_cq->out
+ && cq_next_in == (sw_cq->out = xram_get_8((xram_p)&sw_cq->hw_cq->out))) {
+ SOD(("%d IN %d OUT %d LAST %d\n", qno, sw_cq->in, sw_cq->out, sw_cq->last))
+ SOC_SETIMASK(s, s->imask | (SOC_IMASK_REQ_Q0 << qno));
+ return -EBUSY; // If queue full, just say NO
+ }
+
+ request = sw_cq->pool + sw_cq->in;
+ fch = &request->fchdr;
+
+ switch (fcmd->proto) {
+ case TYPE_SCSI_FCP:
+ request->shdr.token = TOKEN(TYPE_SCSI_FCP, port->mask, fcmd->token);
+ request->data[0].base = fc->dma_scsi_cmd + fcmd->token * sizeof(fcp_cmd);
+ request->data[0].count = sizeof(fcp_cmd);
+ request->data[1].base = fc->dma_scsi_rsp + fcmd->token * fc->rsp_size;
+ request->data[1].count = fc->rsp_size;
+ if (fcmd->data) {
+ request->shdr.segcnt = 3;
+ i = fc->scsi_cmd_pool[fcmd->token].fcp_data_len;
+ request->shdr.bytecnt = i;
+ request->data[2].base = fcmd->data;
+ request->data[2].count = i;
+ request->type = (fc->scsi_cmd_pool[fcmd->token].fcp_cntl & FCP_CNTL_WRITE) ?
+ SOC_CQTYPE_IO_WRITE : SOC_CQTYPE_IO_READ;
+ } else {
+ request->shdr.segcnt = 2;
+ request->shdr.bytecnt = 0;
+ request->data[2].base = 0;
+ request->data[2].count = 0;
+ request->type = SOC_CQTYPE_SIMPLE;
+ }
+ FILL_FCHDR_RCTL_DID(fch, R_CTL_COMMAND, fc->did);
+ FILL_FCHDR_SID(fch, fc->sid);
+ FILL_FCHDR_TYPE_FCTL(fch, TYPE_SCSI_FCP, F_CTL_FIRST_SEQ | F_CTL_SEQ_INITIATIVE);
+ FILL_FCHDR_SEQ_DF_SEQ(fch, 0, 0, 0);
+ FILL_FCHDR_OXRX(fch, 0xffff, 0xffff);
+ fch->param = 0;
+ request->shdr.flags = port->flags;
+ request->shdr.class = 2;
+ break;
+
+ case PROTO_OFFLINE:
+ memset (request, 0, sizeof(*request));
+ request->shdr.token = TOKEN(PROTO_OFFLINE, port->mask, fcmd->token);
+ request->type = SOC_CQTYPE_OFFLINE;
+ FILL_FCHDR_RCTL_DID(fch, R_CTL_COMMAND, fc->did);
+ FILL_FCHDR_SID(fch, fc->sid);
+ FILL_FCHDR_TYPE_FCTL(fch, TYPE_SCSI_FCP, F_CTL_FIRST_SEQ | F_CTL_SEQ_INITIATIVE);
+ FILL_FCHDR_SEQ_DF_SEQ(fch, 0, 0, 0);
+ FILL_FCHDR_OXRX(fch, 0xffff, 0xffff);
+ request->shdr.flags = port->flags;
+ break;
+
+ default:
+ request->shdr.token = TOKEN(fcmd->proto, port->mask, fcmd->token);
+ request->shdr.class = 2;
+ request->shdr.flags = port->flags;
+ memcpy (fch, &fcmd->fch, sizeof(fc_hdr));
+ request->data[0].count = fcmd->cmdlen;
+ request->data[1].count = fcmd->rsplen;
+ request->type = fcmd->class;
+ switch (fcmd->class) {
+ case FC_CLASS_OUTBOUND:
+ request->data[0].base = fcmd->cmd;
+ request->data[0].count = fcmd->cmdlen;
+ request->type = SOC_CQTYPE_OUTBOUND;
+ request->shdr.bytecnt = fcmd->cmdlen;
+ request->shdr.segcnt = 1;
+ break;
+ case FC_CLASS_INBOUND:
+ request->data[0].base = fcmd->rsp;
+ request->data[0].count = fcmd->rsplen;
+ request->type = SOC_CQTYPE_INBOUND;
+ request->shdr.bytecnt = 0;
+ request->shdr.segcnt = 1;
+ break;
+ case FC_CLASS_SIMPLE:
+ request->data[0].base = fcmd->cmd;
+ request->data[1].base = fcmd->rsp;
+ request->data[0].count = fcmd->cmdlen;
+ request->data[1].count = fcmd->rsplen;
+ request->type = SOC_CQTYPE_SIMPLE;
+ request->shdr.bytecnt = fcmd->cmdlen;
+ request->shdr.segcnt = 2;
+ break;
+ case FC_CLASS_IO_READ:
+ case FC_CLASS_IO_WRITE:
+ request->data[0].base = fcmd->cmd;
+ request->data[1].base = fcmd->rsp;
+ request->data[0].count = fcmd->cmdlen;
+ request->data[1].count = fcmd->rsplen;
+ request->type = (fcmd->class == FC_CLASS_IO_READ) ? SOC_CQTYPE_IO_READ : SOC_CQTYPE_IO_WRITE;
+ if (fcmd->data) {
+ request->data[2].base = fcmd->data;
+ request->data[2].count = fcmd->datalen;
+ request->shdr.bytecnt = fcmd->datalen;
+ request->shdr.segcnt = 3;
+ } else {
+ request->shdr.bytecnt = 0;
+ request->shdr.segcnt = 2;
+ }
+ break;
+ }
+ break;
+ }
+
+ request->count = 1;
+ request->flags = 0;
+ request->seqno = sw_cq->seqno;
+
+ /* And now tell the SOC about it */
+
+ if (++sw_cq->in > sw_cq->last) {
+ sw_cq->in = 0;
+ sw_cq->seqno++;
+ }
+
+ SOD(("Putting %08x into cmd\n", SOC_CMD_RSP_QALL | (sw_cq->in << 24) | (SOC_CMD_REQ_Q0 << qno)))
+
+ s->regs->cmd = SOC_CMD_RSP_QALL | (sw_cq->in << 24) | (SOC_CMD_REQ_Q0 << qno);
+ /* Read so that command is completed */
+ s->regs->cmd;
+
+ return 0;
+}
+
+static inline void soc_download_fw(struct soc *s)
+{
+#ifdef HAVE_SOC_UCODE
+ xram_copy_to (s->xram, soc_ucode, sizeof(soc_ucode));
+ xram_bzero (s->xram + (sizeof(soc_ucode)/sizeof(u16)), 32768 - sizeof(soc_ucode));
+#endif
+}
+
+/* Check for what the best SBUS burst we can use happens
+ * to be on this machine.
+ */
+static inline void soc_init_bursts(struct soc *s, struct linux_sbus_device *sdev)
+{
+ int bsizes, bsizes_more;
+
+ bsizes = (prom_getintdefault(sdev->prom_node,"burst-sizes",0xff) & 0xff);
+ bsizes_more = (prom_getintdefault(sdev->my_bus->prom_node, "burst-sizes", 0xff) & 0xff);
+ bsizes &= bsizes_more;
+ if ((bsizes & 0x7f) == 0x7f)
+ s->cfg = SOC_CFG_BURST_64;
+ else if ((bsizes & 0x3f) == 0x3f)
+ s->cfg = SOC_CFG_BURST_32;
+ else if ((bsizes & 0x1f) == 0x1f)
+ s->cfg = SOC_CFG_BURST_16;
+ else
+ s->cfg = SOC_CFG_BURST_4;
+}
+
+static inline void soc_init(struct linux_sbus_device *sdev, int no)
+{
+#ifdef __sparc_v9__
+ struct devid_cookie dcookie;
+#endif
+ unsigned char tmp[60];
+ int propl;
+ struct soc *s;
+ static unsigned version_printed = 0;
+ soc_hw_cq cq[8];
+ int size, i;
+ int irq;
+
+ s = kmalloc (sizeof (struct soc), GFP_KERNEL);
+ if (!s) return;
+ memset (s, 0, sizeof(struct soc));
+ s->soc_no = no;
+
+ SOD(("socs %08lx soc_intr %08lx soc_hw_enque %08x\n", (long)socs, (long)soc_intr, (long)soc_hw_enque))
+ if (version_printed++ == 0)
+ printk (version);
+
+ s->next = socs;
+ socs = s;
+ s->port[0].fc.dev = sdev;
+ s->port[1].fc.dev = sdev;
+ s->port[0].s = s;
+ s->port[1].s = s;
+
+ s->port[0].fc.next = &s->port[1].fc;
+
+ /* World Wide Name of SOC */
+ propl = prom_getproperty (sdev->prom_node, "soc-wwn", tmp, sizeof(tmp));
+ if (propl != sizeof (fc_wwn)) {
+ s->wwn.naaid = NAAID_IEEE;
+ s->wwn.lo = 0x12345678;
+ } else
+ memcpy (&s->wwn, tmp, sizeof (fc_wwn));
+
+ propl = prom_getproperty (sdev->prom_node, "port-wwns", tmp, sizeof(tmp));
+ if (propl != 2 * sizeof (fc_wwn)) {
+ s->port[0].fc.wwn_nport.naaid = NAAID_IEEE_EXT;
+ s->port[0].fc.wwn_nport.hi = s->wwn.hi;
+ s->port[0].fc.wwn_nport.lo = s->wwn.lo;
+ s->port[1].fc.wwn_nport.naaid = NAAID_IEEE_EXT;
+ s->port[1].fc.wwn_nport.nportid = 1;
+ s->port[1].fc.wwn_nport.hi = s->wwn.hi;
+ s->port[1].fc.wwn_nport.lo = s->wwn.lo;
+ } else {
+ memcpy (&s->port[0].fc.wwn_nport, tmp, sizeof (fc_wwn));
+ memcpy (&s->port[1].fc.wwn_nport, tmp + sizeof (fc_wwn), sizeof (fc_wwn));
+ }
+ memcpy (&s->port[0].fc.wwn_node, &s->wwn, sizeof (fc_wwn));
+ memcpy (&s->port[1].fc.wwn_node, &s->wwn, sizeof (fc_wwn));
+ SOD(("Got wwns %08x%08x ports %08x%08x and %08x%08x\n",
+ *(u32 *)&s->port[0].fc.wwn_nport, s->port[0].fc.wwn_nport.lo,
+ *(u32 *)&s->port[0].fc.wwn_nport, s->port[0].fc.wwn_nport.lo,
+ *(u32 *)&s->port[1].fc.wwn_nport, s->port[1].fc.wwn_nport.lo))
+
+ s->port[0].fc.sid = 1;
+ s->port[1].fc.sid = 17;
+ s->port[0].fc.did = 2;
+ s->port[1].fc.did = 18;
+
+ s->port[0].fc.reset = soc_reset;
+ s->port[1].fc.reset = soc_reset;
+
+ /* Setup the reg property for this device. */
+ prom_apply_sbus_ranges(sdev->my_bus, sdev->reg_addrs, sdev->num_registers, sdev);
+
+ if (sdev->num_registers == 1) {
+ /* Probably SunFire onboard SOC */
+ s->xram = (xram_p)
+ sparc_alloc_io (sdev->reg_addrs [0].phys_addr, 0,
+ sdev->reg_addrs [0].reg_size, "soc_xram",
+ sdev->reg_addrs [0].which_io, 0);
+ s->regs = (struct soc_regs *)((char *)s->xram + 0x10000);
+ } else {
+ /* Probably SOC sbus card */
+ s->xram = (xram_p)
+ sparc_alloc_io (sdev->reg_addrs [1].phys_addr, 0,
+ sdev->reg_addrs [1].reg_size, "soc_xram",
+ sdev->reg_addrs [1].which_io, 0);
+ s->regs = (struct soc_regs *)
+ sparc_alloc_io (sdev->reg_addrs [2].phys_addr, 0,
+ sdev->reg_addrs [2].reg_size, "soc_regs",
+ sdev->reg_addrs [2].which_io, 0);
+ }
+
+ soc_init_bursts(s, sdev);
+
+ SOD(("Disabling SOC\n"))
+
+ soc_disable (s);
+
+ irq = sdev->irqs[0].pri;
+
+#ifndef __sparc_v9__
+ if (request_irq (irq, soc_intr, SA_SHIRQ, "SOC", (void *)s)) {
+ soc_printk ("Cannot order irq %d to go\n", irq);
+ return;
+ }
+#else
+ dcookie.real_dev_id = s;
+ dcookie.imap = dcookie.iclr = 0;
+ dcookie.pil = -1;
+ dcookie.bus_cookie = sdev->my_bus;
+ if (request_irq (irq, soc_intr, (SA_SHIRQ | SA_SBUS | SA_DCOOKIE), "SOC", &dcookie)) {
+ soc_printk ("Cannot order irq %d to go\n", irq);
+ return;
+ }
+ irq = dcookie.ret_ino;
+#endif
+
+ SOD(("SOC uses IRQ%d\n", irq))
+
+ s->port[0].fc.irq = irq;
+ s->port[1].fc.irq = irq;
+
+ sprintf (s->port[0].fc.name, "soc%d port A", no);
+ sprintf (s->port[1].fc.name, "soc%d port B", no);
+ s->port[0].flags = SOC_FC_HDR | SOC_PORT_A;
+ s->port[1].flags = SOC_FC_HDR | SOC_PORT_B;
+ s->port[1].mask = (1 << 11);
+
+ s->port[0].fc.hw_enque = soc_hw_enque;
+ s->port[1].fc.hw_enque = soc_hw_enque;
+
+ soc_download_fw (s);
+
+ SOD(("Downloaded firmware\n"))
+
+ /* Now setup xram circular queues */
+ memset (cq, 0, sizeof(cq));
+
+ size = (SOC_CQ_REQ0_SIZE + SOC_CQ_REQ1_SIZE) * sizeof(soc_req);
+ s->req[0].pool = (soc_req *) sparc_dvma_malloc (size, "SOC request queues", &cq[0].address);
+ s->req[1].pool = s->req[0].pool + SOC_CQ_REQ0_SIZE;
+
+ s->req[0].hw_cq = (soc_hw_cq *)(s->xram + SOC_CQ_REQ_OFFSET);
+ s->req[1].hw_cq = (soc_hw_cq *)(s->xram + SOC_CQ_REQ_OFFSET + sizeof(soc_hw_cq) / sizeof(u16));
+ s->rsp[0].hw_cq = (soc_hw_cq *)(s->xram + SOC_CQ_RSP_OFFSET);
+ s->rsp[1].hw_cq = (soc_hw_cq *)(s->xram + SOC_CQ_RSP_OFFSET + sizeof(soc_hw_cq) / sizeof(u16));
+
+ cq[1].address = cq[0].address + (SOC_CQ_REQ0_SIZE * sizeof(soc_req));
+ cq[4].address = 1;
+ cq[5].address = 1;
+ cq[0].last = SOC_CQ_REQ0_SIZE - 1;
+ cq[1].last = SOC_CQ_REQ1_SIZE - 1;
+ cq[4].last = SOC_CQ_RSP0_SIZE - 1;
+ cq[5].last = SOC_CQ_RSP1_SIZE - 1;
+ for (i = 0; i < 8; i++)
+ cq[i].seqno = 1;
+
+ s->req[0].last = SOC_CQ_REQ0_SIZE - 1;
+ s->req[1].last = SOC_CQ_REQ1_SIZE - 1;
+ s->rsp[0].last = SOC_CQ_RSP0_SIZE - 1;
+ s->rsp[1].last = SOC_CQ_RSP1_SIZE - 1;
+
+ s->req[0].seqno = 1;
+ s->req[1].seqno = 1;
+ s->rsp[0].seqno = 1;
+ s->rsp[1].seqno = 1;
+
+ xram_copy_to (s->xram + SOC_CQ_REQ_OFFSET, cq, sizeof(cq));
+
+ /* Make our sw copy of SOC service parameters */
+ xram_copy_from (s->serv_params, s->xram + 0x140, sizeof (s->serv_params));
+
+ s->port[0].fc.common_svc = (common_svc_parm *)s->serv_params;
+ s->port[0].fc.class_svcs = (svc_parm *)(s->serv_params + 0x20);
+ s->port[1].fc.common_svc = (common_svc_parm *)&s->serv_params;
+ s->port[1].fc.class_svcs = (svc_parm *)(s->serv_params + 0x20);
+
+ soc_enable (s);
+
+ SOD(("Enabled SOC\n"))
+}
+
+#ifndef MODULE
+__initfunc(int soc_probe(void))
+#else
+int init_module(void)
+#endif
+{
+ struct linux_sbus *bus;
+ struct linux_sbus_device *sdev = 0;
+ struct soc *s;
+ int cards = 0;
+
+ for_each_sbus(bus) {
+ for_each_sbusdev(sdev, bus) {
+ if(!strcmp(sdev->prom_name, "SUNW,soc")) {
+ soc_init(sdev, cards);
+ cards++;
+ }
+ }
+ }
+ if (!cards) return -EIO;
+
+ for_each_soc(s)
+ if (s->next)
+ s->port[1].fc.next = &s->next->port[0].fc;
+ fcp_init (&socs->port[0].fc);
+ return 0;
+}
+
+EXPORT_NO_SYMBOLS;
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+ struct soc *s;
+ int irq;
+ struct linux_sbus_device *sdev;
+
+ for_each_soc(s) {
+ /* FIXME: Tell FC layer we say good bye */
+ irq = s->port[0].fc.irq;
+ disable_irq (irq);
+ free_irq (irq, s);
+ sdev = s->port[0].fc.dev;
+ if (sdev->num_registers == 1)
+ sparc_free_io ((char *)s->xram, sdev->reg_addrs [0].reg_size);
+ else {
+ sparc_free_io ((char *)s->xram, sdev->reg_addrs [1].reg_size);
+ sparc_free_io ((char *)s->regs, sdev->reg_addrs [2].reg_size);
+ }
+ /* FIXME: sparc_dvma_free() ??? */
+ }
+}
+#endif
diff --git a/drivers/fc4/soc.h b/drivers/fc4/soc.h
new file mode 100644
index 000000000..92435d3af
--- /dev/null
+++ b/drivers/fc4/soc.h
@@ -0,0 +1,275 @@
+/* soc.h: Definitions for Sparc SUNW,soc Fibre Channel Sbus driver.
+ *
+ * Copyright (C) 1996,1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ */
+
+#ifndef __SOC_H
+#define __SOC_H
+
+#include "fc.h"
+#include "fcp.h"
+#include "fcp_scsi.h"
+
+/* Hardware structures and constants first {{{ */
+
+struct soc_regs {
+ volatile u32 cfg; /* Config Register */
+ volatile u32 sae; /* Slave Access Error Register */
+ volatile u32 cmd; /* Command & Status Register */
+ volatile u32 imask; /* Interrupt Mask Register */
+};
+
+/* Config Register */
+#define SOC_CFG_EXT_RAM_BANK_MASK 0x07000000
+#define SOC_CFG_EEPROM_BANK_MASK 0x00030000
+#define SOC_CFG_BURST64_MASK 0x00000700
+#define SOC_CFG_SBUS_PARITY_TEST 0x00000020
+#define SOC_CFG_SBUS_PARITY_CHECK 0x00000010
+#define SOC_CFG_SBUS_ENHANCED 0x00000008
+#define SOC_CFG_BURST_MASK 0x00000007
+/* Bursts */
+#define SOC_CFG_BURST_4 0x00000000
+#define SOC_CFG_BURST_16 0x00000004
+#define SOC_CFG_BURST_32 0x00000005
+#define SOC_CFG_BURST_64 0x00000006
+
+/* Slave Access Error Register */
+#define SOC_SAE_ALIGNMENT 0x00000004
+#define SOC_SAE_UNSUPPORTED 0x00000002
+#define SOC_SAE_PARITY 0x00000001
+
+/* Command & Status Register */
+#define SOC_CMD_RSP_QALL 0x000f0000
+#define SOC_CMD_RSP_Q0 0x00010000
+#define SOC_CMD_RSP_Q1 0x00020000
+#define SOC_CMD_RSP_Q2 0x00040000
+#define SOC_CMD_RSP_Q3 0x00080000
+#define SOC_CMD_REQ_QALL 0x00000f00
+#define SOC_CMD_REQ_Q0 0x00000100
+#define SOC_CMD_REQ_Q1 0x00000200
+#define SOC_CMD_REQ_Q2 0x00000400
+#define SOC_CMD_REQ_Q3 0x00000800
+#define SOC_CMD_SAE 0x00000080
+#define SOC_CMD_INTR_PENDING 0x00000008
+#define SOC_CMD_NON_QUEUED 0x00000004
+#define SOC_CMD_IDLE 0x00000002
+#define SOC_CMD_SOFT_RESET 0x00000001
+
+/* Interrupt Mask Register */
+#define SOC_IMASK_RSP_QALL 0x000f0000
+#define SOC_IMASK_RSP_Q0 0x00010000
+#define SOC_IMASK_RSP_Q1 0x00020000
+#define SOC_IMASK_RSP_Q2 0x00040000
+#define SOC_IMASK_RSP_Q3 0x00080000
+#define SOC_IMASK_REQ_QALL 0x00000f00
+#define SOC_IMASK_REQ_Q0 0x00000100
+#define SOC_IMASK_REQ_Q1 0x00000200
+#define SOC_IMASK_REQ_Q2 0x00000400
+#define SOC_IMASK_REQ_Q3 0x00000800
+#define SOC_IMASK_SAE 0x00000080
+#define SOC_IMASK_NON_QUEUED 0x00000004
+
+#define SOC_INTR(s, cmd) \
+ (((cmd & SOC_CMD_RSP_QALL) | ((~cmd) & SOC_CMD_REQ_QALL)) \
+ & s->imask)
+
+#define SOC_SETIMASK(s, i) \
+ (s)->imask = (i); (s)->regs->imask = (i)
+
+/* XRAM
+ *
+ * This is a 64KB register area. It accepts only halfword access.
+ * That's why here are the following inline functions...
+ */
+
+typedef u16 *xram_p;
+
+/* Get 32bit number from XRAM */
+static inline u32 xram_get_32 (xram_p x)
+{
+ return (((u32)*x) << 16) | (x[1]);
+}
+
+/* Like the above, but when we don't care about the high 16 bits */
+static inline u32 xram_get_32low (xram_p x)
+{
+ return (u32)x[1];
+}
+
+static inline u8 xram_get_8 (xram_p x)
+{
+ if (((long)x) & 1) {
+ x = (xram_p)((long)x - 1);
+ return (u8)*x;
+ } else
+ return (u8)(*x >> 8);
+}
+
+static inline void xram_copy_from (void *p, xram_p x, int len)
+{
+ for (len >>= 2; len > 0; len--, x += 2) {
+ *((u32 *)p)++ = (((u32)(*x)) << 16) | (x[1]);
+ }
+}
+
+static inline void xram_copy_to (xram_p x, void *p, int len)
+{
+ register u32 tmp;
+ for (len >>= 2; len > 0; len--, x += 2) {
+ tmp = *((u32 *)p)++;
+ *x = tmp >> 16;
+ x[1] = tmp;
+ }
+}
+
+static inline void xram_bzero (xram_p x, int len)
+{
+ for (len >>= 1; len > 0; len--) *x++ = 0;
+}
+
+/* Circular Queue */
+
+/* These two are in sizeof(u16) units */
+#define SOC_CQ_REQ_OFFSET 0x100
+#define SOC_CQ_RSP_OFFSET 0x110
+
+typedef struct {
+ u32 address;
+ u8 in;
+ u8 out;
+ u8 last;
+ u8 seqno;
+} soc_hw_cq;
+
+#define SOC_PORT_A 0x0000 /* From/To Port A */
+#define SOC_PORT_B 0x0001 /* From/To Port A */
+#define SOC_FC_HDR 0x0002 /* Contains FC Header */
+#define SOC_NORSP 0x0004 /* Don't generate response nor interrupt */
+#define SOC_NOINT 0x0008 /* Generate response but not interrupt */
+#define SOC_XFERRDY 0x0010 /* Generate XFERRDY */
+#define SOC_IGNOREPARAM 0x0020 /* Ignore PARAM field in the FC header */
+#define SOC_COMPLETE 0x0040 /* Command completed */
+#define SOC_UNSOLICITED 0x0080 /* For request this is the packet to establish unsolicited pools, */
+ /* for rsp this is unsolicited packet */
+#define SOC_STATUS 0x0100 /* State change (on/off line) */
+
+typedef struct {
+ u32 token;
+ u16 flags;
+ u8 class;
+ u8 segcnt;
+ u32 bytecnt;
+} soc_hdr;
+
+typedef struct {
+ u32 base;
+ u32 count;
+} soc_data;
+
+#define SOC_CQTYPE_OUTBOUND 0x01
+#define SOC_CQTYPE_INBOUND 0x02
+#define SOC_CQTYPE_SIMPLE 0x03
+#define SOC_CQTYPE_IO_WRITE 0x04
+#define SOC_CQTYPE_IO_READ 0x05
+#define SOC_CQTYPE_UNSOLICITED 0x06
+#define SOC_CQTYPE_DIAG 0x07
+#define SOC_CQTYPE_OFFLINE 0x08
+#define SOC_CQTYPE_RESPONSE 0x10
+#define SOC_CQTYPE_INLINE 0x20
+
+#define SOC_CQFLAGS_CONT 0x01
+#define SOC_CQFLAGS_FULL 0x02
+#define SOC_CQFLAGS_BADHDR 0x04
+#define SOC_CQFLAGS_BADPKT 0x08
+
+typedef struct {
+ soc_hdr shdr;
+ soc_data data[3];
+ fc_hdr fchdr;
+ u8 count;
+ u8 type;
+ u8 flags;
+ u8 seqno;
+} soc_req;
+
+#define SOC_OK 0
+#define SOC_P_RJT 2
+#define SOC_F_RJT 3
+#define SOC_P_BSY 4
+#define SOC_F_BSY 5
+#define SOC_ONLINE 0x10
+#define SOC_OFFLINE 0x11
+#define SOC_TIMEOUT 0x12
+#define SOC_OVERRUN 0x13
+#define SOC_UNKOWN_CQ_TYPE 0x20
+#define SOC_BAD_SEG_CNT 0x21
+#define SOC_MAX_XCHG_EXCEEDED 0x22
+#define SOC_BAD_XID 0x23
+#define SOC_XCHG_BUSY 0x24
+#define SOC_BAD_POOL_ID 0x25
+#define SOC_INSUFFICIENT_CQES 0x26
+#define SOC_ALLOC_FAIL 0x27
+#define SOC_BAD_SID 0x28
+#define SOC_NO_SEG_INIT 0x29
+
+typedef struct {
+ soc_hdr shdr;
+ u32 status;
+ soc_data data;
+ u8 xxx1[12];
+ fc_hdr fchdr;
+ u8 count;
+ u8 type;
+ u8 flags;
+ u8 seqno;
+} soc_rsp;
+
+/* }}} */
+
+/* Now our software structures and constants we use to drive the beast {{{ */
+
+#define SOC_CQ_REQ0_SIZE 4
+#define SOC_CQ_REQ1_SIZE 64
+#define SOC_CQ_RSP0_SIZE 8
+#define SOC_CQ_RSP1_SIZE 4
+
+#define SOC_SOLICITED_RSP_Q 0
+#define SOC_UNSOLICITED_RSP_Q 1
+
+struct soc;
+
+typedef struct {
+ /* This must come first */
+ fc_channel fc;
+ struct soc *s;
+ u16 flags;
+ u16 mask;
+} soc_port;
+
+typedef struct {
+ soc_hw_cq *hw_cq; /* Related XRAM cq */
+ soc_req *pool;
+ u8 in;
+ u8 out;
+ u8 last;
+ u8 seqno;
+} soc_cq;
+
+struct soc {
+ soc_port port[2]; /* Every SOC has one or two FC ports */
+ soc_cq req[2]; /* Request CQs */
+ soc_cq rsp[2]; /* Response CQs */
+ int soc_no;
+ struct soc_regs *regs;
+ xram_p xram;
+ fc_wwn wwn;
+ u32 imask; /* Our copy of regs->imask */
+ u32 cfg; /* Our copy of regs->cfg */
+ char serv_params[80];
+ struct soc *next;
+ int curr_port; /* Which port will have priority to fcp_queue_empty */
+};
+
+/* }}} */
+
+#endif /* !(__SOC_H) */