aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/fc4
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 18:20:36 -0400
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 18:20:36 -0400
commit1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch)
tree0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/fc4
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history, even though we have it. We can create a separate "historical" git archive of that later if we want to, and in the meantime it's about 3.2GB when imported into git - space that would just make the early git days unnecessarily complicated, when we don't have a lot of good infrastructure for it. Let it rip!
Diffstat (limited to 'drivers/fc4')
-rw-r--r--drivers/fc4/Kconfig81
-rw-r--r--drivers/fc4/Makefile9
-rw-r--r--drivers/fc4/fc-al.h27
-rw-r--r--drivers/fc4/fc.c1158
-rw-r--r--drivers/fc4/fc.h230
-rw-r--r--drivers/fc4/fc_syms.c33
-rw-r--r--drivers/fc4/fcp.h94
-rw-r--r--drivers/fc4/fcp_impl.h164
-rw-r--r--drivers/fc4/soc.c766
-rw-r--r--drivers/fc4/soc.h301
-rw-r--r--drivers/fc4/socal.c906
-rw-r--r--drivers/fc4/socal.h314
12 files changed, 4083 insertions, 0 deletions
diff --git a/drivers/fc4/Kconfig b/drivers/fc4/Kconfig
new file mode 100644
index 000000000000..f00c02a13ed6
--- /dev/null
+++ b/drivers/fc4/Kconfig
@@ -0,0 +1,81 @@
1#
2# FC4 device configuration
3#
4
5menu "Fibre Channel support"
6
7config FC4
8 tristate "Fibre Channel and FC4 SCSI support"
9 ---help---
10 Fibre Channel is a high speed serial protocol mainly used to
11 connect large storage devices to the computer; it is compatible with
12 and intended to replace SCSI.
13
14 This is an experimental support for storage arrays connected to your
15 computer using optical fibre cables and the "X3.269-199X Fibre
16 Channel Protocol for SCSI" specification. If you want to use this,
17 you need to say Y here and to "SCSI support" as well as to the
18 drivers for the storage array itself and for the interface adapter
19 such as SOC or SOC+. This subsystem could even serve for IP
20 networking, with some code extensions.
21
22 If unsure, say N.
23
24comment "FC4 drivers"
25 depends on FC4
26
27config FC4_SOC
28 tristate "Sun SOC/Sbus"
29 depends on FC4!=n && (SPARC32 || SPARC64)
30 help
31 Serial Optical Channel is an interface card with one or two Fibre
32 Optic ports, each of which can be connected to a disk array. Note
33 that if you have older firmware in the card, you'll need the
34 microcode from the Solaris driver to make it work.
35
36 To compile this support as a module, choose M here: the module will
37 be called soc.
38
39config FC4_SOCAL
40 tristate "Sun SOC+ (aka SOCAL)"
41 depends on FC4!=n && (SPARC32 || SPARC64)
42 ---help---
43 Serial Optical Channel Plus is an interface card with up to two
44 Fibre Optic ports. This card supports FC Arbitrated Loop (usually
45 A5000 or internal FC disks in E[3-6]000 machines through the
46 Interface Board). You'll probably need the microcode from the
47 Solaris driver to make it work.
48
49 To compile this support as a module, choose M here: the module will
50 be called socal.
51
52comment "FC4 targets"
53 depends on FC4
54
55config SCSI_PLUTO
56 tristate "SparcSTORAGE Array 100 and 200 series"
57 depends on FC4!=n && SCSI
58 help
59 If you never bought a disk array made by Sun, go with N.
60
61 To compile this support as a module, choose M here: the module will
62 be called pluto.
63
64config SCSI_FCAL
65 tristate "Sun Enterprise Network Array (A5000 and EX500)" if SPARC32 || SPARC64
66 depends on FC4!=n && SCSI
67 help
68 This driver drives FC-AL disks connected through a Fibre Channel
69 card using the drivers/fc4 layer (currently only SOCAL). The most
70 common is either A5000 array or internal disks in E[3-6]000
71 machines.
72
73 To compile this support as a module, choose M here: the module will
74 be called fcal.
75
76config SCSI_FCAL
77 prompt "Generic FC-AL disk driver"
78 depends on FC4!=n && SCSI && !SPARC32 && !SPARC64
79
80endmenu
81
diff --git a/drivers/fc4/Makefile b/drivers/fc4/Makefile
new file mode 100644
index 000000000000..0db3fbb553e9
--- /dev/null
+++ b/drivers/fc4/Makefile
@@ -0,0 +1,9 @@
1#
2# Makefile for the Linux Fibre Channel device drivers.
3#
4
5fc4-objs := fc.o fc_syms.o
6
7obj-$(CONFIG_FC4) += fc4.o
8obj-$(CONFIG_FC4_SOC) += soc.o
9obj-$(CONFIG_FC4_SOCAL) += socal.o
diff --git a/drivers/fc4/fc-al.h b/drivers/fc4/fc-al.h
new file mode 100644
index 000000000000..62d3ca436d72
--- /dev/null
+++ b/drivers/fc4/fc-al.h
@@ -0,0 +1,27 @@
1/* fc-al.h: Definitions for Fibre Channel Arbitrated Loop topology.
2 *
3 * Copyright (C) 1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
4 *
5 * Sources:
6 * Fibre Channel Arbitrated Loop (FC-AL), ANSI, Rev. 4.5, 1995
7 */
8
9#ifndef __FC_AL_H
10#define __FC_AL_H
11
12/* Loop initialization payloads */
13#define FC_AL_LISM 0x11010000 /* Select Master, 12B payload */
14#define FC_AL_LIFA 0x11020000 /* Fabric Assign AL_PA bitmap, 20B payload */
15#define FC_AL_LIPA 0x11030000 /* Previously Acquired AL_PA bitmap, 20B payload */
16#define FC_AL_LIHA 0x11040000 /* Hard Assigned AL_PA bitmap, 20B payload */
17#define FC_AL_LISA 0x11050000 /* Soft Assigned AL_PA bitmap, 20B payload */
18#define FC_AL_LIRP 0x11060000 /* Report AL_PA position map, 132B payload */
19#define FC_AL_LILP 0x11070000 /* Loop AL_PA position map, 132B payload */
20
21typedef struct {
22 u32 magic;
23 u8 len;
24 u8 alpa[127];
25} fc_al_posmap;
26
27#endif /* !(__FC_H) */
diff --git a/drivers/fc4/fc.c b/drivers/fc4/fc.c
new file mode 100644
index 000000000000..1fbb219aa9ba
--- /dev/null
+++ b/drivers/fc4/fc.c
@@ -0,0 +1,1158 @@
1/* fc.c: Generic Fibre Channel and FC4 SCSI driver.
2 *
3 * Copyright (C) 1997,1998,1999 Jakub Jelinek (jj@ultra.linux.cz)
4 * Copyright (C) 1997,1998 Jirka Hanika (geo@ff.cuni.cz)
5 *
6 * There are two kinds of Fibre Channel adapters used in Linux. Either
7 * the adapter is "smart" and does all FC bookkeeping by itself and
8 * just presents a standard SCSI interface to the operating system
9 * (that's e.g. the case with Qlogic FC cards), or leaves most of the FC
10 * bookkeeping to the OS (e.g. soc, socal). Drivers for the former adapters
11 * will look like normal SCSI drivers (with the exception of max_id will be
12 * usually 127), the latter on the other side allows SCSI, IP over FC and other
13 * protocols. This driver tree is for the latter adapters.
14 *
15 * This file should support both Point-to-Point and Arbitrated Loop topologies.
16 *
17 * Sources:
18 * Fibre Channel Physical & Signaling Interface (FC-PH), dpANS, 1994
19 * dpANS Fibre Channel Protocol for SCSI (X3.269-199X), Rev. 012, 1995
20 * Fibre Channel Arbitrated Loop (FC-AL), Rev. 4.5, 1995
21 * Fibre Channel Private Loop SCSI Direct Attach (FC-PLDA), Rev. 2.1, 1997
22 */
23
24#include <linux/module.h>
25#include <linux/kernel.h>
26#include <linux/jiffies.h>
27#include <linux/types.h>
28#include <linux/fcntl.h>
29#include <linux/interrupt.h>
30#include <linux/ptrace.h>
31#include <linux/ioport.h>
32#include <linux/in.h>
33#include <linux/slab.h>
34#include <linux/string.h>
35#include <linux/init.h>
36
37#include <asm/pgtable.h>
38#include <asm/irq.h>
39#include <asm/semaphore.h>
40#include "fcp_impl.h"
41#include <scsi/scsi_host.h>
42
43/* #define FCDEBUG */
44
45#define fc_printk printk ("%s: ", fc->name); printk
46
47#ifdef FCDEBUG
48#define FCD(x) fc_printk x;
49#define FCND(x) printk ("FC: "); printk x;
50#else
51#define FCD(x)
52#define FCND(x)
53#endif
54
55#ifdef __sparc__
56#define dma_alloc_consistent(d,s,p) sbus_alloc_consistent(d,s,p)
57#define dma_free_consistent(d,s,v,h) sbus_free_consistent(d,s,v,h)
58#define dma_map_single(d,v,s,dir) sbus_map_single(d,v,s,dir)
59#define dma_unmap_single(d,h,s,dir) sbus_unmap_single(d,h,s,dir)
60#define dma_map_sg(d,s,n,dir) sbus_map_sg(d,s,n,dir)
61#define dma_unmap_sg(d,s,n,dir) sbus_unmap_sg(d,s,n,dir)
62#else
63#define dma_alloc_consistent(d,s,p) pci_alloc_consistent(d,s,p)
64#define dma_free_consistent(d,s,v,h) pci_free_consistent(d,s,v,h)
65#define dma_map_single(d,v,s,dir) pci_map_single(d,v,s,dir)
66#define dma_unmap_single(d,h,s,dir) pci_unmap_single(d,h,s,dir)
67#define dma_map_sg(d,s,n,dir) pci_map_sg(d,s,n,dir)
68#define dma_unmap_sg(d,s,n,dir) pci_unmap_sg(d,s,n,dir)
69#endif
70
71#define FCP_CMND(SCpnt) ((fcp_cmnd *)&(SCpnt->SCp))
72#define FC_SCMND(SCpnt) ((fc_channel *)(SCpnt->device->host->hostdata[0]))
73#define SC_FCMND(fcmnd) ((Scsi_Cmnd *)((long)fcmnd - (long)&(((Scsi_Cmnd *)0)->SCp)))
74
75static int fcp_scsi_queue_it(fc_channel *, Scsi_Cmnd *, fcp_cmnd *, int);
76void fcp_queue_empty(fc_channel *);
77
78static void fcp_scsi_insert_queue (fc_channel *fc, fcp_cmnd *fcmd)
79{
80 if (!fc->scsi_que) {
81 fc->scsi_que = fcmd;
82 fcmd->next = fcmd;
83 fcmd->prev = fcmd;
84 } else {
85 fc->scsi_que->prev->next = fcmd;
86 fcmd->prev = fc->scsi_que->prev;
87 fc->scsi_que->prev = fcmd;
88 fcmd->next = fc->scsi_que;
89 }
90}
91
92static void fcp_scsi_remove_queue (fc_channel *fc, fcp_cmnd *fcmd)
93{
94 if (fcmd == fcmd->next) {
95 fc->scsi_que = NULL;
96 return;
97 }
98 if (fcmd == fc->scsi_que)
99 fc->scsi_que = fcmd->next;
100 fcmd->prev->next = fcmd->next;
101 fcmd->next->prev = fcmd->prev;
102}
103
104fc_channel *fc_channels = NULL;
105
106#define LSMAGIC 620829043
107typedef struct {
108 /* Must be first */
109 struct semaphore sem;
110 int magic;
111 int count;
112 logi *logi;
113 fcp_cmnd *fcmds;
114 atomic_t todo;
115 struct timer_list timer;
116 unsigned char grace[0];
117} ls;
118
119#define LSOMAGIC 654907799
120typedef struct {
121 /* Must be first */
122 struct semaphore sem;
123 int magic;
124 int count;
125 fcp_cmnd *fcmds;
126 atomic_t todo;
127 struct timer_list timer;
128} lso;
129
130#define LSEMAGIC 84482456
131typedef struct {
132 /* Must be first */
133 struct semaphore sem;
134 int magic;
135 int status;
136 struct timer_list timer;
137} lse;
138
139static void fcp_login_timeout(unsigned long data)
140{
141 ls *l = (ls *)data;
142 FCND(("Login timeout\n"))
143 up(&l->sem);
144}
145
146static void fcp_login_done(fc_channel *fc, int i, int status)
147{
148 fcp_cmnd *fcmd;
149 logi *plogi;
150 fc_hdr *fch;
151 ls *l = (ls *)fc->ls;
152
153 FCD(("Login done %d %d\n", i, status))
154 if (i < l->count) {
155 if (fc->state == FC_STATE_FPORT_OK) {
156 FCD(("Additional FPORT_OK received with status %d\n", status))
157 return;
158 }
159 switch (status) {
160 case FC_STATUS_OK: /* Oh, we found a fabric */
161 case FC_STATUS_P_RJT: /* Oh, we haven't found any */
162 fc->state = FC_STATE_FPORT_OK;
163 fcmd = l->fcmds + i;
164 plogi = l->logi + 3 * i;
165 dma_unmap_single (fc->dev, fcmd->cmd, 3 * sizeof(logi),
166 DMA_BIDIRECTIONAL);
167 plogi->code = LS_PLOGI;
168 memcpy (&plogi->nport_wwn, &fc->wwn_nport, sizeof(fc_wwn));
169 memcpy (&plogi->node_wwn, &fc->wwn_node, sizeof(fc_wwn));
170 memcpy (&plogi->common, fc->common_svc, sizeof(common_svc_parm));
171 memcpy (&plogi->class1, fc->class_svcs, 3*sizeof(svc_parm));
172 fch = &fcmd->fch;
173 fcmd->token += l->count;
174 FILL_FCHDR_RCTL_DID(fch, R_CTL_ELS_REQ, fc->did);
175 FILL_FCHDR_SID(fch, fc->sid);
176#ifdef FCDEBUG
177 {
178 int i;
179 unsigned *x = (unsigned *)plogi;
180 printk ("logi: ");
181 for (i = 0; i < 21; i++)
182 printk ("%08x ", x[i]);
183 printk ("\n");
184 }
185#endif
186 fcmd->cmd = dma_map_single (fc->dev, plogi, 3 * sizeof(logi),
187 DMA_BIDIRECTIONAL);
188 fcmd->rsp = fcmd->cmd + 2 * sizeof(logi);
189 if (fc->hw_enque (fc, fcmd))
190 printk ("FC: Cannot enque PLOGI packet on %s\n", fc->name);
191 break;
192 case FC_STATUS_ERR_OFFLINE:
193 fc->state = FC_STATE_MAYBEOFFLINE;
194 FCD (("FC is offline %d\n", l->grace[i]))
195 break;
196 default:
197 printk ("FLOGI failed for %s with status %d\n", fc->name, status);
198 /* Do some sort of error recovery here */
199 break;
200 }
201 } else {
202 i -= l->count;
203 if (fc->state != FC_STATE_FPORT_OK) {
204 FCD(("Unexpected N-PORT rsp received"))
205 return;
206 }
207 switch (status) {
208 case FC_STATUS_OK:
209 plogi = l->logi + 3 * i;
210 dma_unmap_single (fc->dev, l->fcmds[i].cmd, 3 * sizeof(logi),
211 DMA_BIDIRECTIONAL);
212 if (!fc->wwn_dest.lo && !fc->wwn_dest.hi) {
213 memcpy (&fc->wwn_dest, &plogi[1].node_wwn, sizeof(fc_wwn));
214 FCD(("Dest WWN %08x%08x\n", *(u32 *)&fc->wwn_dest, fc->wwn_dest.lo))
215 } else if (fc->wwn_dest.lo != plogi[1].node_wwn.lo ||
216 fc->wwn_dest.hi != plogi[1].node_wwn.hi) {
217 printk ("%s: mismatch in wwns. Got %08x%08x, expected %08x%08x\n",
218 fc->name,
219 *(u32 *)&plogi[1].node_wwn, plogi[1].node_wwn.lo,
220 *(u32 *)&fc->wwn_dest, fc->wwn_dest.lo);
221 }
222 fc->state = FC_STATE_ONLINE;
223 printk ("%s: ONLINE\n", fc->name);
224 if (atomic_dec_and_test (&l->todo))
225 up(&l->sem);
226 break;
227 case FC_STATUS_ERR_OFFLINE:
228 fc->state = FC_STATE_OFFLINE;
229 dma_unmap_single (fc->dev, l->fcmds[i].cmd, 3 * sizeof(logi),
230 DMA_BIDIRECTIONAL);
231 printk ("%s: FC is offline\n", fc->name);
232 if (atomic_dec_and_test (&l->todo))
233 up(&l->sem);
234 break;
235 default:
236 printk ("PLOGI failed for %s with status %d\n", fc->name, status);
237 /* Do some sort of error recovery here */
238 break;
239 }
240 }
241}
242
243static void fcp_report_map_done(fc_channel *fc, int i, int status)
244{
245 fcp_cmnd *fcmd;
246 fc_hdr *fch;
247 unsigned char j;
248 ls *l = (ls *)fc->ls;
249 fc_al_posmap *p;
250
251 FCD(("Report map done %d %d\n", i, status))
252 switch (status) {
253 case FC_STATUS_OK: /* Ok, let's have a fun on a loop */
254 dma_unmap_single (fc->dev, l->fcmds[i].cmd, 3 * sizeof(logi),
255 DMA_BIDIRECTIONAL);
256 p = (fc_al_posmap *)(l->logi + 3 * i);
257#ifdef FCDEBUG
258 {
259 u32 *u = (u32 *)p;
260 FCD(("%08x\n", u[0]))
261 u ++;
262 FCD(("%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x\n", u[0],u[1],u[2],u[3],u[4],u[5],u[6],u[7]))
263 }
264#endif
265 if ((p->magic & 0xffff0000) != FC_AL_LILP || !p->len) {
266 printk ("FC: Bad magic from REPORT_AL_MAP on %s - %08x\n", fc->name, p->magic);
267 fc->state = FC_STATE_OFFLINE;
268 } else {
269 fc->posmap = (fcp_posmap *)kmalloc(sizeof(fcp_posmap)+p->len, GFP_KERNEL);
270 if (!fc->posmap) {
271 printk("FC: Not enough memory, offlining channel\n");
272 fc->state = FC_STATE_OFFLINE;
273 } else {
274 int k;
275 memset(fc->posmap, 0, sizeof(fcp_posmap)+p->len);
276 /* FIXME: This is where SOCAL transfers our AL-PA.
277 Keep it here till we found out what other cards do... */
278 fc->sid = (p->magic & 0xff);
279 for (i = 0; i < p->len; i++)
280 if (p->alpa[i] == fc->sid)
281 break;
282 k = p->len;
283 if (i == p->len)
284 i = 0;
285 else {
286 p->len--;
287 i++;
288 }
289 fc->posmap->len = p->len;
290 for (j = 0; j < p->len; j++) {
291 if (i == k) i = 0;
292 fc->posmap->list[j] = p->alpa[i++];
293 }
294 fc->state = FC_STATE_ONLINE;
295 }
296 }
297 printk ("%s: ONLINE\n", fc->name);
298 if (atomic_dec_and_test (&l->todo))
299 up(&l->sem);
300 break;
301 case FC_STATUS_POINTTOPOINT: /* We're Point-to-Point, no AL... */
302 FCD(("SID %d DID %d\n", fc->sid, fc->did))
303 fcmd = l->fcmds + i;
304 dma_unmap_single(fc->dev, fcmd->cmd, 3 * sizeof(logi),
305 DMA_BIDIRECTIONAL);
306 fch = &fcmd->fch;
307 memset(l->logi + 3 * i, 0, 3 * sizeof(logi));
308 FILL_FCHDR_RCTL_DID(fch, R_CTL_ELS_REQ, FS_FABRIC_F_PORT);
309 FILL_FCHDR_SID(fch, 0);
310 FILL_FCHDR_TYPE_FCTL(fch, TYPE_EXTENDED_LS, F_CTL_FIRST_SEQ | F_CTL_SEQ_INITIATIVE);
311 FILL_FCHDR_SEQ_DF_SEQ(fch, 0, 0, 0);
312 FILL_FCHDR_OXRX(fch, 0xffff, 0xffff);
313 fch->param = 0;
314 l->logi [3 * i].code = LS_FLOGI;
315 fcmd->cmd = dma_map_single (fc->dev, l->logi + 3 * i, 3 * sizeof(logi),
316 DMA_BIDIRECTIONAL);
317 fcmd->rsp = fcmd->cmd + sizeof(logi);
318 fcmd->cmdlen = sizeof(logi);
319 fcmd->rsplen = sizeof(logi);
320 fcmd->data = (dma_addr_t)NULL;
321 fcmd->class = FC_CLASS_SIMPLE;
322 fcmd->proto = TYPE_EXTENDED_LS;
323 if (fc->hw_enque (fc, fcmd))
324 printk ("FC: Cannot enque FLOGI packet on %s\n", fc->name);
325 break;
326 case FC_STATUS_ERR_OFFLINE:
327 fc->state = FC_STATE_MAYBEOFFLINE;
328 FCD (("FC is offline %d\n", l->grace[i]))
329 break;
330 default:
331 printk ("FLOGI failed for %s with status %d\n", fc->name, status);
332 /* Do some sort of error recovery here */
333 break;
334 }
335}
336
337void fcp_register(fc_channel *fc, u8 type, int unregister)
338{
339 int size, i;
340 int slots = (fc->can_queue * 3) >> 1;
341
342 FCND(("Going to %sregister\n", unregister ? "un" : ""))
343
344 if (type == TYPE_SCSI_FCP) {
345 if (!unregister) {
346 fc->scsi_cmd_pool = (fcp_cmd *)
347 dma_alloc_consistent (fc->dev,
348 slots * (sizeof (fcp_cmd) + fc->rsp_size),
349 &fc->dma_scsi_cmd);
350 fc->scsi_rsp_pool = (char *)(fc->scsi_cmd_pool + slots);
351 fc->dma_scsi_rsp = fc->dma_scsi_cmd + slots * sizeof (fcp_cmd);
352 fc->scsi_bitmap_end = (slots + 63) & ~63;
353 size = fc->scsi_bitmap_end / 8;
354 fc->scsi_bitmap = kmalloc (size, GFP_KERNEL);
355 memset (fc->scsi_bitmap, 0, size);
356 set_bit (0, fc->scsi_bitmap);
357 for (i = fc->can_queue; i < fc->scsi_bitmap_end; i++)
358 set_bit (i, fc->scsi_bitmap);
359 fc->scsi_free = fc->can_queue;
360 fc->cmd_slots = (fcp_cmnd **)kmalloc(slots * sizeof(fcp_cmnd*), GFP_KERNEL);
361 memset(fc->cmd_slots, 0, slots * sizeof(fcp_cmnd*));
362 fc->abort_count = 0;
363 } else {
364 fc->scsi_name[0] = 0;
365 kfree (fc->scsi_bitmap);
366 kfree (fc->cmd_slots);
367 FCND(("Unregistering\n"));
368 if (fc->rst_pkt) {
369 if (fc->rst_pkt->eh_state == SCSI_STATE_UNUSED)
370 kfree(fc->rst_pkt);
371 else {
372 /* Can't happen. Some memory would be lost. */
373 printk("FC: Reset in progress. Now?!");
374 }
375 }
376 FCND(("Unregistered\n"));
377 }
378 } else
379 printk ("FC: %segistering unknown type %02x\n", unregister ? "Unr" : "R", type);
380}
381
382static void fcp_scsi_done(Scsi_Cmnd *SCpnt);
383
384static inline void fcp_scsi_receive(fc_channel *fc, int token, int status, fc_hdr *fch)
385{
386 fcp_cmnd *fcmd;
387 fcp_rsp *rsp;
388 int host_status;
389 Scsi_Cmnd *SCpnt;
390 int sense_len;
391 int rsp_status;
392
393 fcmd = fc->cmd_slots[token];
394 if (!fcmd) return;
395 rsp = (fcp_rsp *) (fc->scsi_rsp_pool + fc->rsp_size * token);
396 SCpnt = SC_FCMND(fcmd);
397
398 if (SCpnt->done != fcp_scsi_done)
399 return;
400
401 rsp_status = rsp->fcp_status;
402 FCD(("rsp_status %08x status %08x\n", rsp_status, status))
403 switch (status) {
404 case FC_STATUS_OK:
405 host_status=DID_OK;
406
407 if (rsp_status & FCP_STATUS_RESID) {
408#ifdef FCDEBUG
409 FCD(("Resid %d\n", rsp->fcp_resid))
410 {
411 fcp_cmd *cmd = fc->scsi_cmd_pool + token;
412 int i;
413
414 printk ("Command ");
415 for (i = 0; i < sizeof(fcp_cmd); i+=4)
416 printk ("%08x ", *(u32 *)(((char *)cmd)+i));
417 printk ("\nResponse ");
418 for (i = 0; i < fc->rsp_size; i+=4)
419 printk ("%08x ", *(u32 *)(((char *)rsp)+i));
420 printk ("\n");
421 }
422#endif
423 }
424
425 if (rsp_status & FCP_STATUS_SENSE_LEN) {
426 sense_len = rsp->fcp_sense_len;
427 if (sense_len > sizeof(SCpnt->sense_buffer)) sense_len = sizeof(SCpnt->sense_buffer);
428 memcpy(SCpnt->sense_buffer, ((char *)(rsp+1)), sense_len);
429 }
430
431 if (fcmd->data) {
432 if (SCpnt->use_sg)
433 dma_unmap_sg(fc->dev, (struct scatterlist *)SCpnt->buffer,
434 SCpnt->use_sg,
435 SCpnt->sc_data_direction);
436 else
437 dma_unmap_single(fc->dev, fcmd->data, SCpnt->request_bufflen,
438 SCpnt->sc_data_direction);
439 }
440 break;
441 default:
442 host_status=DID_ERROR; /* FIXME */
443 FCD(("Wrong FC status %d for token %d\n", status, token))
444 break;
445 }
446
447 if (status_byte(rsp_status) == QUEUE_FULL) {
448 printk ("%s: (%d,%d) Received rsp_status 0x%x\n", fc->name, SCpnt->device->channel, SCpnt->device->id, rsp_status);
449 }
450
451 SCpnt->result = (host_status << 16) | (rsp_status & 0xff);
452#ifdef FCDEBUG
453 if (host_status || SCpnt->result || rsp_status) printk("FC: host_status %d, packet status %d\n",
454 host_status, SCpnt->result);
455#endif
456 SCpnt->done = fcmd->done;
457 fcmd->done=NULL;
458 clear_bit(token, fc->scsi_bitmap);
459 fc->scsi_free++;
460 FCD(("Calling scsi_done with %08x\n", SCpnt->result))
461 SCpnt->scsi_done(SCpnt);
462}
463
464void fcp_receive_solicited(fc_channel *fc, int proto, int token, int status, fc_hdr *fch)
465{
466 int magic;
467 FCD(("receive_solicited %d %d %d\n", proto, token, status))
468 switch (proto) {
469 case TYPE_SCSI_FCP:
470 fcp_scsi_receive(fc, token, status, fch); break;
471 case TYPE_EXTENDED_LS:
472 case PROTO_REPORT_AL_MAP:
473 magic = 0;
474 if (fc->ls)
475 magic = ((ls *)(fc->ls))->magic;
476 if (magic == LSMAGIC) {
477 ls *l = (ls *)fc->ls;
478 int i = (token >= l->count) ? token - l->count : token;
479
480 /* Let's be sure */
481 if ((unsigned)i < l->count && l->fcmds[i].fc == fc) {
482 if (proto == TYPE_EXTENDED_LS)
483 fcp_login_done(fc, token, status);
484 else
485 fcp_report_map_done(fc, token, status);
486 break;
487 }
488 }
489 FCD(("fc %p fc->ls %p fc->cmd_slots %p\n", fc, fc->ls, fc->cmd_slots))
490 if (proto == TYPE_EXTENDED_LS && !fc->ls && fc->cmd_slots) {
491 fcp_cmnd *fcmd;
492
493 fcmd = fc->cmd_slots[token];
494 if (fcmd && fcmd->ls && ((ls *)(fcmd->ls))->magic == LSEMAGIC) {
495 lse *l = (lse *)fcmd->ls;
496
497 l->status = status;
498 up (&l->sem);
499 }
500 }
501 break;
502 case PROTO_OFFLINE:
503 if (fc->ls && ((lso *)(fc->ls))->magic == LSOMAGIC) {
504 lso *l = (lso *)fc->ls;
505
506 if ((unsigned)token < l->count && l->fcmds[token].fc == fc) {
507 /* Wow, OFFLINE response arrived :) */
508 FCD(("OFFLINE Response arrived\n"))
509 fc->state = FC_STATE_OFFLINE;
510 if (atomic_dec_and_test (&l->todo))
511 up(&l->sem);
512 }
513 }
514 break;
515
516 default:
517 break;
518 }
519}
520
521void fcp_state_change(fc_channel *fc, int state)
522{
523 FCD(("state_change %d %d\n", state, fc->state))
524 if (state == FC_STATE_ONLINE && fc->state == FC_STATE_MAYBEOFFLINE)
525 fc->state = FC_STATE_UNINITED;
526 else if (state == FC_STATE_ONLINE)
527 printk (KERN_WARNING "%s: state change to ONLINE\n", fc->name);
528 else
529 printk (KERN_ERR "%s: state change to OFFLINE\n", fc->name);
530}
531
532int fcp_initialize(fc_channel *fcchain, int count)
533{
534 fc_channel *fc;
535 fcp_cmnd *fcmd;
536 int i, retry, ret;
537 ls *l;
538
539 FCND(("fcp_inititialize %08lx\n", (long)fcp_init))
540 FCND(("fc_channels %08lx\n", (long)fc_channels))
541 FCND((" SID %d DID %d\n", fcchain->sid, fcchain->did))
542 l = kmalloc(sizeof (ls) + count, GFP_KERNEL);
543 if (!l) {
544 printk ("FC: Cannot allocate memory for initialization\n");
545 return -ENOMEM;
546 }
547 memset (l, 0, sizeof(ls) + count);
548 l->magic = LSMAGIC;
549 l->count = count;
550 FCND(("FCP Init for %d channels\n", count))
551 init_MUTEX_LOCKED(&l->sem);
552 init_timer(&l->timer);
553 l->timer.function = fcp_login_timeout;
554 l->timer.data = (unsigned long)l;
555 atomic_set (&l->todo, count);
556 l->logi = kmalloc (count * 3 * sizeof(logi), GFP_KERNEL);
557 l->fcmds = kmalloc (count * sizeof(fcp_cmnd), GFP_KERNEL);
558 if (!l->logi || !l->fcmds) {
559 if (l->logi) kfree (l->logi);
560 if (l->fcmds) kfree (l->fcmds);
561 kfree (l);
562 printk ("FC: Cannot allocate DMA memory for initialization\n");
563 return -ENOMEM;
564 }
565 memset (l->logi, 0, count * 3 * sizeof(logi));
566 memset (l->fcmds, 0, count * sizeof(fcp_cmnd));
567 for (fc = fcchain, i = 0; fc && i < count; fc = fc->next, i++) {
568 fc->state = FC_STATE_UNINITED;
569 fc->rst_pkt = NULL; /* kmalloc when first used */
570 }
571 /* First try if we are in a AL topology */
572 FCND(("Initializing REPORT_MAP packets\n"))
573 for (fc = fcchain, i = 0; fc && i < count; fc = fc->next, i++) {
574 fcmd = l->fcmds + i;
575 fc->login = fcmd;
576 fc->ls = (void *)l;
577 /* Assumes sizeof(fc_al_posmap) < 3 * sizeof(logi), which is true */
578 fcmd->cmd = dma_map_single (fc->dev, l->logi + 3 * i, 3 * sizeof(logi),
579 DMA_BIDIRECTIONAL);
580 fcmd->proto = PROTO_REPORT_AL_MAP;
581 fcmd->token = i;
582 fcmd->fc = fc;
583 }
584 for (retry = 0; retry < 8; retry++) {
585 int nqueued = 0;
586 FCND(("Sending REPORT_MAP/FLOGI/PLOGI packets\n"))
587 for (fc = fcchain, i = 0; fc && i < count; fc = fc->next, i++) {
588 if (fc->state == FC_STATE_ONLINE || fc->state == FC_STATE_OFFLINE)
589 continue;
590 disable_irq(fc->irq);
591 if (fc->state == FC_STATE_MAYBEOFFLINE) {
592 if (!l->grace[i]) {
593 l->grace[i]++;
594 FCD(("Grace\n"))
595 } else {
596 fc->state = FC_STATE_OFFLINE;
597 enable_irq(fc->irq);
598 dma_unmap_single (fc->dev, l->fcmds[i].cmd, 3 * sizeof(logi), DMA_BIDIRECTIONAL);
599 if (atomic_dec_and_test (&l->todo))
600 goto all_done;
601 }
602 }
603 ret = fc->hw_enque (fc, fc->login);
604 enable_irq(fc->irq);
605 if (!ret) {
606 nqueued++;
607 continue;
608 }
609 if (ret == -ENOSYS && fc->login->proto == PROTO_REPORT_AL_MAP) {
610 /* Oh yes, this card handles Point-to-Point only, so let's try that. */
611 fc_hdr *fch;
612
613 FCD(("SID %d DID %d\n", fc->sid, fc->did))
614 fcmd = l->fcmds + i;
615 dma_unmap_single(fc->dev, fcmd->cmd, 3 * sizeof(logi), DMA_BIDIRECTIONAL);
616 fch = &fcmd->fch;
617 FILL_FCHDR_RCTL_DID(fch, R_CTL_ELS_REQ, FS_FABRIC_F_PORT);
618 FILL_FCHDR_SID(fch, 0);
619 FILL_FCHDR_TYPE_FCTL(fch, TYPE_EXTENDED_LS, F_CTL_FIRST_SEQ | F_CTL_SEQ_INITIATIVE);
620 FILL_FCHDR_SEQ_DF_SEQ(fch, 0, 0, 0);
621 FILL_FCHDR_OXRX(fch, 0xffff, 0xffff);
622 fch->param = 0;
623 l->logi [3 * i].code = LS_FLOGI;
624 fcmd->cmd = dma_map_single (fc->dev, l->logi + 3 * i, 3 * sizeof(logi), DMA_BIDIRECTIONAL);
625 fcmd->rsp = fcmd->cmd + sizeof(logi);
626 fcmd->cmdlen = sizeof(logi);
627 fcmd->rsplen = sizeof(logi);
628 fcmd->data = (dma_addr_t)NULL;
629 fcmd->class = FC_CLASS_SIMPLE;
630 fcmd->proto = TYPE_EXTENDED_LS;
631 } else
632 printk ("FC: Cannot enque FLOGI/REPORT_MAP packet on %s\n", fc->name);
633 }
634
635 if (nqueued) {
636 l->timer.expires = jiffies + 5 * HZ;
637 add_timer(&l->timer);
638
639 down(&l->sem);
640 if (!atomic_read(&l->todo)) {
641 FCND(("All channels answered in time\n"))
642 break; /* All fc channels have answered us */
643 }
644 }
645 }
646all_done:
647 for (fc = fcchain, i = 0; fc && i < count; fc = fc->next, i++) {
648 fc->ls = NULL;
649 switch (fc->state) {
650 case FC_STATE_ONLINE: break;
651 case FC_STATE_OFFLINE: break;
652 default: dma_unmap_single (fc->dev, l->fcmds[i].cmd, 3 * sizeof(logi), DMA_BIDIRECTIONAL);
653 break;
654 }
655 }
656 del_timer(&l->timer);
657 kfree (l->logi);
658 kfree (l->fcmds);
659 kfree (l);
660 return 0;
661}
662
663int fcp_forceoffline(fc_channel *fcchain, int count)
664{
665 fc_channel *fc;
666 fcp_cmnd *fcmd;
667 int i, ret;
668 lso l;
669
670 memset (&l, 0, sizeof(lso));
671 l.count = count;
672 l.magic = LSOMAGIC;
673 FCND(("FCP Force Offline for %d channels\n", count))
674 init_MUTEX_LOCKED(&l.sem);
675 init_timer(&l.timer);
676 l.timer.function = fcp_login_timeout;
677 l.timer.data = (unsigned long)&l;
678 atomic_set (&l.todo, count);
679 l.fcmds = kmalloc (count * sizeof(fcp_cmnd), GFP_KERNEL);
680 if (!l.fcmds) {
681 kfree (l.fcmds);
682 printk ("FC: Cannot allocate memory for forcing offline\n");
683 return -ENOMEM;
684 }
685 memset (l.fcmds, 0, count * sizeof(fcp_cmnd));
686 FCND(("Initializing OFFLINE packets\n"))
687 for (fc = fcchain, i = 0; fc && i < count; fc = fc->next, i++) {
688 fc->state = FC_STATE_UNINITED;
689 fcmd = l.fcmds + i;
690 fc->login = fcmd;
691 fc->ls = (void *)&l;
692 fcmd->did = fc->did;
693 fcmd->class = FC_CLASS_OFFLINE;
694 fcmd->proto = PROTO_OFFLINE;
695 fcmd->token = i;
696 fcmd->fc = fc;
697 disable_irq(fc->irq);
698 ret = fc->hw_enque (fc, fc->login);
699 enable_irq(fc->irq);
700 if (ret) printk ("FC: Cannot enque OFFLINE packet on %s\n", fc->name);
701 }
702
703 l.timer.expires = jiffies + 5 * HZ;
704 add_timer(&l.timer);
705 down(&l.sem);
706 del_timer(&l.timer);
707
708 for (fc = fcchain, i = 0; fc && i < count; fc = fc->next, i++)
709 fc->ls = NULL;
710 kfree (l.fcmds);
711 return 0;
712}
713
714int fcp_init(fc_channel *fcchain)
715{
716 fc_channel *fc;
717 int count=0;
718 int ret;
719
720 for (fc = fcchain; fc; fc = fc->next) {
721 fc->fcp_register = fcp_register;
722 count++;
723 }
724
725 ret = fcp_initialize (fcchain, count);
726 if (ret)
727 return ret;
728
729 if (!fc_channels)
730 fc_channels = fcchain;
731 else {
732 for (fc = fc_channels; fc->next; fc = fc->next);
733 fc->next = fcchain;
734 }
735 return ret;
736}
737
738void fcp_release(fc_channel *fcchain, int count) /* count must > 0 */
739{
740 fc_channel *fc;
741 fc_channel *fcx;
742
743 for (fc = fcchain; --count && fc->next; fc = fc->next);
744 if (count) {
745 printk("FC: nothing to release\n");
746 return;
747 }
748
749 if (fc_channels == fcchain)
750 fc_channels = fc->next;
751 else {
752 for (fcx = fc_channels; fcx->next != fcchain; fcx = fcx->next);
753 fcx->next = fc->next;
754 }
755 fc->next = NULL;
756
757 /*
758 * We've just grabbed fcchain out of the fc_channel list
759 * and zero-terminated it, while destroying the count.
760 *
761 * Freeing the fc's is the low level driver's responsibility.
762 */
763}
764
765
766static void fcp_scsi_done (Scsi_Cmnd *SCpnt)
767{
768 unsigned long flags;
769
770 spin_lock_irqsave(SCpnt->device->host->host_lock, flags);
771 if (FCP_CMND(SCpnt)->done)
772 FCP_CMND(SCpnt)->done(SCpnt);
773 spin_unlock_irqrestore(SCpnt->device->host->host_lock, flags);
774}
775
776static int fcp_scsi_queue_it(fc_channel *fc, Scsi_Cmnd *SCpnt, fcp_cmnd *fcmd, int prepare)
777{
778 long i;
779 fcp_cmd *cmd;
780 u32 fcp_cntl;
781 if (prepare) {
782 i = find_first_zero_bit (fc->scsi_bitmap, fc->scsi_bitmap_end);
783 set_bit (i, fc->scsi_bitmap);
784 fcmd->token = i;
785 cmd = fc->scsi_cmd_pool + i;
786
787 if (fc->encode_addr (SCpnt, cmd->fcp_addr, fc, fcmd)) {
788 /* Invalid channel/id/lun and couldn't map it into fcp_addr */
789 clear_bit (i, fc->scsi_bitmap);
790 SCpnt->result = (DID_BAD_TARGET << 16);
791 SCpnt->scsi_done(SCpnt);
792 return 0;
793 }
794 fc->scsi_free--;
795 fc->cmd_slots[fcmd->token] = fcmd;
796
797 if (SCpnt->device->tagged_supported) {
798 if (jiffies - fc->ages[SCpnt->device->channel * fc->targets + SCpnt->device->id] > (5 * 60 * HZ)) {
799 fc->ages[SCpnt->device->channel * fc->targets + SCpnt->device->id] = jiffies;
800 fcp_cntl = FCP_CNTL_QTYPE_ORDERED;
801 } else
802 fcp_cntl = FCP_CNTL_QTYPE_SIMPLE;
803 } else
804 fcp_cntl = FCP_CNTL_QTYPE_UNTAGGED;
805 if (!SCpnt->request_bufflen && !SCpnt->use_sg) {
806 cmd->fcp_cntl = fcp_cntl;
807 fcmd->data = (dma_addr_t)NULL;
808 } else {
809 switch (SCpnt->cmnd[0]) {
810 case WRITE_6:
811 case WRITE_10:
812 case WRITE_12:
813 cmd->fcp_cntl = (FCP_CNTL_WRITE | fcp_cntl); break;
814 default:
815 cmd->fcp_cntl = (FCP_CNTL_READ | fcp_cntl); break;
816 }
817 if (!SCpnt->use_sg) {
818 cmd->fcp_data_len = SCpnt->request_bufflen;
819 fcmd->data = dma_map_single (fc->dev, (char *)SCpnt->request_buffer,
820 SCpnt->request_bufflen,
821 SCpnt->sc_data_direction);
822 } else {
823 struct scatterlist *sg = (struct scatterlist *)SCpnt->buffer;
824 int nents;
825
826 FCD(("XXX: Use_sg %d %d\n", SCpnt->use_sg, sg->length))
827 nents = dma_map_sg (fc->dev, sg, SCpnt->use_sg,
828 SCpnt->sc_data_direction);
829 if (nents > 1) printk ("%s: SG for nents %d (use_sg %d) not handled yet\n", fc->name, nents, SCpnt->use_sg);
830 fcmd->data = sg_dma_address(sg);
831 cmd->fcp_data_len = sg_dma_len(sg);
832 }
833 }
834 memcpy (cmd->fcp_cdb, SCpnt->cmnd, SCpnt->cmd_len);
835 memset (cmd->fcp_cdb+SCpnt->cmd_len, 0, sizeof(cmd->fcp_cdb)-SCpnt->cmd_len);
836 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)))
837 }
838 FCD(("Trying to enque %p\n", fcmd))
839 if (!fc->scsi_que) {
840 if (!fc->hw_enque (fc, fcmd)) {
841 FCD(("hw_enque succeeded for %p\n", fcmd))
842 return 0;
843 }
844 }
845 FCD(("Putting into que1 %p\n", fcmd))
846 fcp_scsi_insert_queue (fc, fcmd);
847 return 0;
848}
849
850int fcp_scsi_queuecommand(Scsi_Cmnd *SCpnt, void (* done)(Scsi_Cmnd *))
851{
852 fcp_cmnd *fcmd = FCP_CMND(SCpnt);
853 fc_channel *fc = FC_SCMND(SCpnt);
854
855 FCD(("Entering SCSI queuecommand %p\n", fcmd))
856 if (SCpnt->done != fcp_scsi_done) {
857 fcmd->done = SCpnt->done;
858 SCpnt->done = fcp_scsi_done;
859 SCpnt->scsi_done = done;
860 fcmd->proto = TYPE_SCSI_FCP;
861 if (!fc->scsi_free) {
862 FCD(("FC: !scsi_free, putting cmd on ML queue\n"))
863#if (FCP_SCSI_USE_NEW_EH_CODE == 0)
864 printk("fcp_scsi_queue_command: queue full, losing cmd, bad\n");
865#endif
866 return 1;
867 }
868 return fcp_scsi_queue_it(fc, SCpnt, fcmd, 1);
869 }
870 return fcp_scsi_queue_it(fc, SCpnt, fcmd, 0);
871}
872
873void fcp_queue_empty(fc_channel *fc)
874{
875 fcp_cmnd *fcmd;
876
877 FCD(("Queue empty\n"))
878 while ((fcmd = fc->scsi_que)) {
879 /* The hw told us we can try again queue some packet */
880 if (fc->hw_enque (fc, fcmd))
881 break;
882 fcp_scsi_remove_queue (fc, fcmd);
883 }
884}
885
886int fcp_scsi_abort(Scsi_Cmnd *SCpnt)
887{
888 /* Internal bookkeeping only. Lose 1 cmd_slots slot. */
889 fcp_cmnd *fcmd = FCP_CMND(SCpnt);
890 fc_channel *fc = FC_SCMND(SCpnt);
891
892 /*
893 * We react to abort requests by simply forgetting
894 * about the command and pretending everything's sweet.
895 * This may or may not be silly. We can't, however,
896 * immediately reuse the command's cmd_slots slot,
897 * as its result may arrive later and we cannot
898 * check whether it is the aborted one, can't we?
899 *
900 * Therefore, after the first few aborts are done,
901 * we tell the scsi error handler to do something clever.
902 * It will eventually call host reset, refreshing
903 * cmd_slots for us.
904 *
905 * There is a theoretical chance that we sometimes allow
906 * more than can_queue packets to the jungle this way,
907 * but the worst outcome possible is a series of
908 * more aborts and eventually the dev_reset catharsis.
909 */
910
911 if (++fc->abort_count < (fc->can_queue >> 1)) {
912 unsigned long flags;
913
914 SCpnt->result = DID_ABORT;
915 spin_lock_irqsave(SCpnt->device->host->host_lock, flags);
916 fcmd->done(SCpnt);
917 spin_unlock_irqrestore(SCpnt->device->host->host_lock, flags);
918 printk("FC: soft abort\n");
919 return SUCCESS;
920 } else {
921 printk("FC: hard abort refused\n");
922 return FAILED;
923 }
924}
925
926void fcp_scsi_reset_done(Scsi_Cmnd *SCpnt)
927{
928 fc_channel *fc = FC_SCMND(SCpnt);
929
930 fc->rst_pkt->eh_state = SCSI_STATE_FINISHED;
931 up(fc->rst_pkt->device->host->eh_action);
932}
933
934#define FCP_RESET_TIMEOUT (2*HZ)
935
936int fcp_scsi_dev_reset(Scsi_Cmnd *SCpnt)
937{
938 fcp_cmd *cmd;
939 fcp_cmnd *fcmd;
940 fc_channel *fc = FC_SCMND(SCpnt);
941 DECLARE_MUTEX_LOCKED(sem);
942
943 if (!fc->rst_pkt) {
944 fc->rst_pkt = (Scsi_Cmnd *) kmalloc(sizeof(SCpnt), GFP_KERNEL);
945 if (!fc->rst_pkt) return FAILED;
946
947 fcmd = FCP_CMND(fc->rst_pkt);
948
949
950 fcmd->token = 0;
951 cmd = fc->scsi_cmd_pool + 0;
952 FCD(("Preparing rst packet\n"))
953 fc->encode_addr (SCpnt, cmd->fcp_addr, fc, fcmd);
954 fc->rst_pkt->device = SCpnt->device;
955 fc->rst_pkt->cmd_len = 0;
956
957 fc->cmd_slots[0] = fcmd;
958
959 cmd->fcp_cntl = FCP_CNTL_QTYPE_ORDERED | FCP_CNTL_RESET;
960 fcmd->data = (dma_addr_t)NULL;
961 fcmd->proto = TYPE_SCSI_FCP;
962
963 memcpy (cmd->fcp_cdb, SCpnt->cmnd, SCpnt->cmd_len);
964 memset (cmd->fcp_cdb+SCpnt->cmd_len, 0, sizeof(cmd->fcp_cdb)-SCpnt->cmd_len);
965 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)))
966 } else {
967 fcmd = FCP_CMND(fc->rst_pkt);
968 if (fc->rst_pkt->eh_state == SCSI_STATE_QUEUED)
969 return FAILED; /* or SUCCESS. Only these */
970 }
971 fc->rst_pkt->done = NULL;
972
973
974 fc->rst_pkt->eh_state = SCSI_STATE_QUEUED;
975 init_timer(&fc->rst_pkt->eh_timeout);
976 fc->rst_pkt->eh_timeout.data = (unsigned long) fc->rst_pkt;
977 fc->rst_pkt->eh_timeout.expires = jiffies + FCP_RESET_TIMEOUT;
978 fc->rst_pkt->eh_timeout.function = (void (*)(unsigned long))fcp_scsi_reset_done;
979
980 add_timer(&fc->rst_pkt->eh_timeout);
981
982 /*
983 * Set up the semaphore so we wait for the command to complete.
984 */
985
986 fc->rst_pkt->device->host->eh_action = &sem;
987 fc->rst_pkt->request->rq_status = RQ_SCSI_BUSY;
988
989 fc->rst_pkt->done = fcp_scsi_reset_done;
990 fcp_scsi_queue_it(fc, fc->rst_pkt, fcmd, 0);
991
992 down(&sem);
993
994 fc->rst_pkt->device->host->eh_action = NULL;
995 del_timer(&fc->rst_pkt->eh_timeout);
996
997 /*
998 * See if timeout. If so, tell the host to forget about it.
999 * In other words, we don't want a callback any more.
1000 */
1001 if (fc->rst_pkt->eh_state == SCSI_STATE_TIMEOUT ) {
1002 fc->rst_pkt->eh_state = SCSI_STATE_UNUSED;
1003 return FAILED;
1004 }
1005 fc->rst_pkt->eh_state = SCSI_STATE_UNUSED;
1006 return SUCCESS;
1007}
1008
1009int fcp_scsi_bus_reset(Scsi_Cmnd *SCpnt)
1010{
1011 printk ("FC: bus reset!\n");
1012 return FAILED;
1013}
1014
1015int fcp_scsi_host_reset(Scsi_Cmnd *SCpnt)
1016{
1017 fc_channel *fc = FC_SCMND(SCpnt);
1018 fcp_cmnd *fcmd = FCP_CMND(SCpnt);
1019 int i;
1020
1021 printk ("FC: host reset\n");
1022
1023 for (i=0; i < fc->can_queue; i++) {
1024 if (fc->cmd_slots[i] && SCpnt->result != DID_ABORT) {
1025 SCpnt->result = DID_RESET;
1026 fcmd->done(SCpnt);
1027 fc->cmd_slots[i] = NULL;
1028 }
1029 }
1030 fc->reset(fc);
1031 fc->abort_count = 0;
1032 if (fcp_initialize(fc, 1)) return SUCCESS;
1033 else return FAILED;
1034}
1035
1036static int fcp_els_queue_it(fc_channel *fc, fcp_cmnd *fcmd)
1037{
1038 long i;
1039
1040 i = find_first_zero_bit (fc->scsi_bitmap, fc->scsi_bitmap_end);
1041 set_bit (i, fc->scsi_bitmap);
1042 fcmd->token = i;
1043 fc->scsi_free--;
1044 fc->cmd_slots[fcmd->token] = fcmd;
1045 return fcp_scsi_queue_it(fc, NULL, fcmd, 0);
1046}
1047
1048static int fc_do_els(fc_channel *fc, unsigned int alpa, void *data, int len)
1049{
1050 fcp_cmnd _fcmd, *fcmd;
1051 fc_hdr *fch;
1052 lse l;
1053 int i;
1054
1055 fcmd = &_fcmd;
1056 memset(fcmd, 0, sizeof(fcmd));
1057 FCD(("PLOGI SID %d DID %d\n", fc->sid, alpa))
1058 fch = &fcmd->fch;
1059 FILL_FCHDR_RCTL_DID(fch, R_CTL_ELS_REQ, alpa);
1060 FILL_FCHDR_SID(fch, fc->sid);
1061 FILL_FCHDR_TYPE_FCTL(fch, TYPE_EXTENDED_LS, F_CTL_FIRST_SEQ | F_CTL_SEQ_INITIATIVE);
1062 FILL_FCHDR_SEQ_DF_SEQ(fch, 0, 0, 0);
1063 FILL_FCHDR_OXRX(fch, 0xffff, 0xffff);
1064 fch->param = 0;
1065 fcmd->cmd = dma_map_single (fc->dev, data, 2 * len, DMA_BIDIRECTIONAL);
1066 fcmd->rsp = fcmd->cmd + len;
1067 fcmd->cmdlen = len;
1068 fcmd->rsplen = len;
1069 fcmd->data = (dma_addr_t)NULL;
1070 fcmd->fc = fc;
1071 fcmd->class = FC_CLASS_SIMPLE;
1072 fcmd->proto = TYPE_EXTENDED_LS;
1073
1074 memset (&l, 0, sizeof(lse));
1075 l.magic = LSEMAGIC;
1076 init_MUTEX_LOCKED(&l.sem);
1077 l.timer.function = fcp_login_timeout;
1078 l.timer.data = (unsigned long)&l;
1079 l.status = FC_STATUS_TIMED_OUT;
1080 fcmd->ls = (void *)&l;
1081
1082 disable_irq(fc->irq);
1083 fcp_els_queue_it(fc, fcmd);
1084 enable_irq(fc->irq);
1085
1086 for (i = 0;;) {
1087 l.timer.expires = jiffies + 5 * HZ;
1088 add_timer(&l.timer);
1089 down(&l.sem);
1090 del_timer(&l.timer);
1091 if (l.status != FC_STATUS_TIMED_OUT) break;
1092 if (++i == 3) break;
1093 disable_irq(fc->irq);
1094 fcp_scsi_queue_it(fc, NULL, fcmd, 0);
1095 enable_irq(fc->irq);
1096 }
1097
1098 clear_bit(fcmd->token, fc->scsi_bitmap);
1099 fc->scsi_free++;
1100 dma_unmap_single (fc->dev, fcmd->cmd, 2 * len, DMA_BIDIRECTIONAL);
1101 return l.status;
1102}
1103
1104int fc_do_plogi(fc_channel *fc, unsigned char alpa, fc_wwn *node, fc_wwn *nport)
1105{
1106 logi *l;
1107 int status;
1108
1109 l = (logi *)kmalloc(2 * sizeof(logi), GFP_KERNEL);
1110 if (!l) return -ENOMEM;
1111 memset(l, 0, 2 * sizeof(logi));
1112 l->code = LS_PLOGI;
1113 memcpy (&l->nport_wwn, &fc->wwn_nport, sizeof(fc_wwn));
1114 memcpy (&l->node_wwn, &fc->wwn_node, sizeof(fc_wwn));
1115 memcpy (&l->common, fc->common_svc, sizeof(common_svc_parm));
1116 memcpy (&l->class1, fc->class_svcs, 3*sizeof(svc_parm));
1117 status = fc_do_els(fc, alpa, l, sizeof(logi));
1118 if (status == FC_STATUS_OK) {
1119 if (l[1].code == LS_ACC) {
1120#ifdef FCDEBUG
1121 u32 *u = (u32 *)&l[1].nport_wwn;
1122 FCD(("AL-PA %02x: Port WWN %08x%08x Node WWN %08x%08x\n", alpa,
1123 u[0], u[1], u[2], u[3]))
1124#endif
1125 memcpy(nport, &l[1].nport_wwn, sizeof(fc_wwn));
1126 memcpy(node, &l[1].node_wwn, sizeof(fc_wwn));
1127 } else
1128 status = FC_STATUS_BAD_RSP;
1129 }
1130 kfree(l);
1131 return status;
1132}
1133
1134typedef struct {
1135 unsigned int code;
1136 unsigned params[4];
1137} prli;
1138
1139int fc_do_prli(fc_channel *fc, unsigned char alpa)
1140{
1141 prli *p;
1142 int status;
1143
1144 p = (prli *)kmalloc(2 * sizeof(prli), GFP_KERNEL);
1145 if (!p) return -ENOMEM;
1146 memset(p, 0, 2 * sizeof(prli));
1147 p->code = LS_PRLI;
1148 p->params[0] = 0x08002000;
1149 p->params[3] = 0x00000022;
1150 status = fc_do_els(fc, alpa, p, sizeof(prli));
1151 if (status == FC_STATUS_OK && p[1].code != LS_PRLI_ACC && p[1].code != LS_ACC)
1152 status = FC_STATUS_BAD_RSP;
1153 kfree(p);
1154 return status;
1155}
1156
1157MODULE_LICENSE("GPL");
1158
diff --git a/drivers/fc4/fc.h b/drivers/fc4/fc.h
new file mode 100644
index 000000000000..13f89d4c8cb9
--- /dev/null
+++ b/drivers/fc4/fc.h
@@ -0,0 +1,230 @@
1/* fc.h: Definitions for Fibre Channel Physical and Signaling Interface.
2 *
3 * Copyright (C) 1996-1997,1999 Jakub Jelinek (jj@ultra.linux.cz)
4 *
5 * Sources:
6 * Fibre Channel Physical & Signaling Interface (FC-PH), dpANS, 1994
7 * dpANS Fibre Channel Protocol for SCSI (X3.269-199X), Rev. 012, 1995
8 */
9
10#ifndef __FC_H
11#define __FC_H
12
13/* World Wide Name */
14#define NAAID_IEEE 1
15#define NAAID_IEEE_EXT 2
16#define NAAID_LOCAL 3
17#define NAAID_IP 4
18#define NAAID_IEEE_REG 5
19#define NAAID_IEEE_REG_EXT 6
20#define NAAID_CCITT 12
21#define NAAID_CCITT_GRP 14
22
23/* This is NAAID_IEEE_EXT scheme */
24typedef struct {
25 u32 naaid:4;
26 u32 nportid:12;
27 u32 hi:16;
28 u32 lo;
29} fc_wwn;
30
31/* Frame header for FC-PH frames */
32
33/* r_ctl field */
34#define R_CTL_DEVICE_DATA 0x00 /* FC4 Device_Data frame */
35#define R_CTL_EXTENDED_SVC 0x20 /* Extended Link_Data frame */
36#define R_CTL_FC4_SVC 0x30 /* FC4 Link_Data frame */
37#define R_CTL_VIDEO 0x40 /* Video_Data frame */
38#define R_CTL_BASIC_SVC 0x80 /* Basic Link_Data frame */
39#define R_CTL_LINK_CTL 0xc0 /* Link_Control frame */
40/* FC4 Device_Data frames */
41#define R_CTL_UNCATEGORIZED 0x00
42#define R_CTL_SOLICITED_DATA 0x01
43#define R_CTL_UNSOL_CONTROL 0x02
44#define R_CTL_SOLICITED_CONTROL 0x03
45#define R_CTL_UNSOL_DATA 0x04
46#define R_CTL_XFER_RDY 0x05
47#define R_CTL_COMMAND 0x06
48#define R_CTL_STATUS 0x07
49/* Basic Link_Data frames */
50#define R_CTL_LS_NOP 0x80
51#define R_CTL_LS_ABTS 0x81
52#define R_CTL_LS_RMC 0x82
53#define R_CTL_LS_BA_ACC 0x84
54#define R_CTL_LS_BA_RJT 0x85
55/* Extended Link_Data frames */
56#define R_CTL_ELS_REQ 0x22
57#define R_CTL_ELS_RSP 0x23
58/* Link_Control frames */
59#define R_CTL_ACK_1 0xc0
60#define R_CTL_ACK_N 0xc1
61#define R_CTL_P_RJT 0xc2
62#define R_CTL_F_RJT 0xc3
63#define R_CTL_P_BSY 0xc4
64#define R_CTL_F_BSY_DF 0xc5
65#define R_CTL_F_BSY_LC 0xc6
66#define R_CTL_LCR 0xc7
67
68/* type field */
69#define TYPE_BASIC_LS 0x00
70#define TYPE_EXTENDED_LS 0x01
71#define TYPE_IS8802 0x04
72#define TYPE_IS8802_SNAP 0x05
73#define TYPE_SCSI_FCP 0x08
74#define TYPE_SCSI_GPP 0x09
75#define TYPE_HIPP_FP 0x0a
76#define TYPE_IPI3_MASTER 0x11
77#define TYPE_IPI3_SLAVE 0x12
78#define TYPE_IPI3_PEER 0x13
79
80/* f_ctl field */
81#define F_CTL_FILL_BYTES 0x000003
82#define F_CTL_XCHG_REASSEMBLE 0x000004
83#define F_CTL_RO_PRESENT 0x000008
84#define F_CTL_ABORT_SEQ 0x000030
85#define F_CTL_CONTINUE_SEQ 0x0000c0
86#define F_CTL_INVALIDATE_XID 0x004000
87#define F_CTL_XID_REASSIGNED 0x008000
88#define F_CTL_SEQ_INITIATIVE 0x010000
89#define F_CTL_CHAINED_SEQ 0x020000
90#define F_CTL_END_CONNECT 0x040000
91#define F_CTL_END_SEQ 0x080000
92#define F_CTL_LAST_SEQ 0x100000
93#define F_CTL_FIRST_SEQ 0x200000
94#define F_CTL_SEQ_CONTEXT 0x400000
95#define F_CTL_XCHG_CONTEXT 0x800000
96
97typedef struct {
98 u32 r_ctl:8, did:24;
99 u32 xxx1:8, sid:24;
100 u32 type:8, f_ctl:24;
101 u32 seq_id:8, df_ctl:8, seq_cnt:16;
102 u16 ox_id, rx_id;
103 u32 param;
104} fc_hdr;
105/* The following are ugly macros to make setup of this structure faster */
106#define FILL_FCHDR_RCTL_DID(fch, r_ctl, did) *(u32 *)(fch) = ((r_ctl) << 24) | (did);
107#define FILL_FCHDR_SID(fch, sid) *((u32 *)(fch)+1) = (sid);
108#define FILL_FCHDR_TYPE_FCTL(fch, type, f_ctl) *((u32 *)(fch)+2) = ((type) << 24) | (f_ctl);
109#define FILL_FCHDR_SEQ_DF_SEQ(fch, seq_id, df_ctl, seq_cnt) *((u32 *)(fch)+3) = ((seq_id) << 24) | ((df_ctl) << 16) | (seq_cnt);
110#define FILL_FCHDR_OXRX(fch, ox_id, rx_id) *((u32 *)(fch)+4) = ((ox_id) << 16) | (rx_id);
111
112/* Well known addresses */
113#define FS_GENERAL_MULTICAST 0xfffff7
114#define FS_WELL_KNOWN_MULTICAST 0xfffff8
115#define FS_HUNT_GROUP 0xfffff9
116#define FS_MANAGEMENT_SERVER 0xfffffa
117#define FS_TIME_SERVER 0xfffffb
118#define FS_NAME_SERVER 0xfffffc
119#define FS_FABRIC_CONTROLLER 0xfffffd
120#define FS_FABRIC_F_PORT 0xfffffe
121#define FS_BROADCAST 0xffffff
122
123/* Reject frames */
124/* The param field should be cast to this structure */
125typedef struct {
126 u8 action;
127 u8 reason;
128 u8 xxx;
129 u8 vendor_unique;
130} rjt_param;
131
132/* Reject action codes */
133#define RJT_RETRY 0x01
134#define RJT_NONRETRY 0x02
135
136/* Reject reason codes */
137#define RJT_INVALID_DID 0x01
138#define RJT_INVALID_SID 0x02
139#define RJT_NPORT_NOT_AVAIL_TEMP 0x03
140#define RJT_NPORT_NOT_AVAIL_PERM 0x04
141#define RJT_CLASS_NOT_SUPPORTED 0x05
142#define RJT_DELIMITER_ERROR 0x06
143#define RJT_TYPE_NOT_SUPPORTED 0x07
144#define RJT_INVALID_LINK_CONTROL 0x08
145#define RJT_INVALID_R_CTL 0x09
146#define RJT_INVALID_F_CTL 0x0a
147#define RJT_INVALID_OX_ID 0x0b
148#define RJT_INVALID_RX_ID 0x0c
149#define RJT_INVALID_SEQ_ID 0x0d
150#define RJT_INVALID_DF_CTL 0x0e
151#define RJT_INVALID_SEQ_CNT 0x0f
152#define RJT_INVALID_PARAMETER 0x10
153#define RJT_EXCHANGE_ERROR 0x11
154#define RJT_PROTOCOL_ERROR 0x12
155#define RJT_INCORRECT_LENGTH 0x13
156#define RJT_UNEXPECTED_ACK 0x14
157#define RJT_UNEXPECTED_LINK_RESP 0x15
158#define RJT_LOGIN_REQUIRED 0x16
159#define RJT_EXCESSIVE_SEQUENCES 0x17
160#define RJT_CANT_ESTABLISH_EXCHANGE 0x18
161#define RJT_SECURITY_NOT_SUPPORTED 0x19
162#define RJT_FABRIC_NA 0x1a
163#define RJT_VENDOR_UNIQUE 0xff
164
165
166#define SP_F_PORT_LOGIN 0x10
167
168/* Extended SVC commands */
169#define LS_RJT 0x01000000
170#define LS_ACC 0x02000000
171#define LS_PRLI_ACC 0x02100014
172#define LS_PLOGI 0x03000000
173#define LS_FLOGI 0x04000000
174#define LS_LOGO 0x05000000
175#define LS_ABTX 0x06000000
176#define LS_RCS 0x07000000
177#define LS_RES 0x08000000
178#define LS_RSS 0x09000000
179#define LS_RSI 0x0a000000
180#define LS_ESTS 0x0b000000
181#define LS_ESTC 0x0c000000
182#define LS_ADVC 0x0d000000
183#define LS_RTV 0x0e000000
184#define LS_RLS 0x0f000000
185#define LS_ECHO 0x10000000
186#define LS_TEST 0x11000000
187#define LS_RRQ 0x12000000
188#define LS_IDENT 0x20000000
189#define LS_PRLI 0x20100014
190#define LS_DISPLAY 0x21000000
191#define LS_PRLO 0x21100014
192#define LS_PDISC 0x50000000
193#define LS_ADISC 0x52000000
194
195typedef struct {
196 u8 fcph_hi, fcph_lo;
197 u16 buf2buf_credit;
198 u8 common_features;
199 u8 xxx1;
200 u16 buf2buf_size;
201 u8 xxx2;
202 u8 total_concurrent;
203 u16 off_by_info;
204 u32 e_d_tov;
205} common_svc_parm;
206
207typedef struct {
208 u16 serv_opts;
209 u16 initiator_ctl;
210 u16 rcpt_ctl;
211 u16 recv_size;
212 u8 xxx1;
213 u8 concurrent_seqs;
214 u16 end2end_credit;
215 u16 open_seqs_per_xchg;
216 u16 xxx2;
217} svc_parm;
218
219/* Login */
220typedef struct {
221 u32 code;
222 common_svc_parm common;
223 fc_wwn nport_wwn;
224 fc_wwn node_wwn;
225 svc_parm class1;
226 svc_parm class2;
227 svc_parm class3;
228} logi;
229
230#endif /* !(__FC_H) */
diff --git a/drivers/fc4/fc_syms.c b/drivers/fc4/fc_syms.c
new file mode 100644
index 000000000000..8bac2c453027
--- /dev/null
+++ b/drivers/fc4/fc_syms.c
@@ -0,0 +1,33 @@
1/*
2 * We should not even be trying to compile this if we are not doing
3 * a module.
4 */
5#include <linux/config.h>
6#include <linux/module.h>
7
8#ifdef CONFIG_MODULES
9
10#include <linux/sched.h>
11#include <linux/types.h>
12#include <linux/string.h>
13#include <linux/kernel.h>
14
15#include "fcp_impl.h"
16
17EXPORT_SYMBOL(fcp_init);
18EXPORT_SYMBOL(fcp_release);
19EXPORT_SYMBOL(fcp_queue_empty);
20EXPORT_SYMBOL(fcp_receive_solicited);
21EXPORT_SYMBOL(fc_channels);
22EXPORT_SYMBOL(fcp_state_change);
23EXPORT_SYMBOL(fc_do_plogi);
24EXPORT_SYMBOL(fc_do_prli);
25
26/* SCSI stuff */
27EXPORT_SYMBOL(fcp_scsi_queuecommand);
28EXPORT_SYMBOL(fcp_scsi_abort);
29EXPORT_SYMBOL(fcp_scsi_dev_reset);
30EXPORT_SYMBOL(fcp_scsi_bus_reset);
31EXPORT_SYMBOL(fcp_scsi_host_reset);
32
33#endif /* CONFIG_MODULES */
diff --git a/drivers/fc4/fcp.h b/drivers/fc4/fcp.h
new file mode 100644
index 000000000000..6aa34a7a4c11
--- /dev/null
+++ b/drivers/fc4/fcp.h
@@ -0,0 +1,94 @@
1/* fcp.h: Definitions for Fibre Channel Protocol.
2 *
3 * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
4 *
5 */
6
7#ifndef __FCP_H
8#define __FCP_H
9
10/* FCP addressing is hierarchical with up to 4 layers, MS first.
11 Exact meaning of the addresses is up to the vendor */
12
13/* fcp_cntl field */
14#define FCP_CNTL_WRITE 0x00000001 /* Initiator write */
15#define FCP_CNTL_READ 0x00000002 /* Initiator read */
16#define FCP_CNTL_ABORT_TSK 0x00000200 /* Abort task set */
17#define FCP_CNTL_CLR_TASK 0x00000400 /* Clear task set */
18#define FCP_CNTL_RESET 0x00002000 /* Reset */
19#define FCP_CNTL_CLR_ACA 0x00004000 /* Clear ACA */
20#define FCP_CNTL_KILL_TASK 0x00008000 /* Terminate task */
21#define FCP_CNTL_QTYPE_MASK 0x00070000 /* Tagged queueing type */
22#define FCP_CNTL_QTYPE_SIMPLE 0x00000000
23#define FCP_CNTL_QTYPE_HEAD_OF_Q 0x00010000
24#define FCP_CNTL_QTYPE_ORDERED 0x00020000
25#define FCP_CNTL_QTYPE_ACA_Q_TAG 0x00040000
26#define FCP_CNTL_QTYPE_UNTAGGED 0x00050000
27
28typedef struct {
29 u16 fcp_addr[4];
30 u32 fcp_cntl;
31 u8 fcp_cdb[16];
32 u32 fcp_data_len;
33} fcp_cmd;
34
35/* fcp_status field */
36#define FCP_STATUS_MASK 0x000000ff /* scsi status of command */
37#define FCP_STATUS_RSP_LEN 0x00000100 /* response_len != 0 */
38#define FCP_STATUS_SENSE_LEN 0x00000200 /* sense_len != 0 */
39#define FCP_STATUS_RESID 0x00000400 /* resid != 0 */
40
41typedef struct {
42 u32 xxx[2];
43 u32 fcp_status;
44 u32 fcp_resid;
45 u32 fcp_sense_len;
46 u32 fcp_response_len;
47 /* u8 fcp_sense[fcp_sense_len]; */
48 /* u8 fcp_response[fcp_response_len]; */
49} fcp_rsp;
50
51/* fcp errors */
52
53/* rsp_info_type field */
54#define FCP_RSP_SCSI_BUS_ERR 0x01
55#define FCP_RSP_SCSI_PORT_ERR 0x02
56#define FCP_RSP_CARD_ERR 0x03
57
58/* isp_status field */
59#define FCP_RSP_CMD_COMPLETE 0x0000
60#define FCP_RSP_CMD_INCOMPLETE 0x0001
61#define FCP_RSP_CMD_DMA_ERR 0x0002
62#define FCP_RSP_CMD_TRAN_ERR 0x0003
63#define FCP_RSP_CMD_RESET 0x0004
64#define FCP_RSP_CMD_ABORTED 0x0005
65#define FCP_RSP_CMD_TIMEOUT 0x0006
66#define FCP_RSP_CMD_OVERRUN 0x0007
67
68/* isp_state_flags field */
69#define FCP_RSP_ST_GOT_BUS 0x0100
70#define FCP_RSP_ST_GOT_TARGET 0x0200
71#define FCP_RSP_ST_SENT_CMD 0x0400
72#define FCP_RSP_ST_XFRD_DATA 0x0800
73#define FCP_RSP_ST_GOT_STATUS 0x1000
74#define FCP_RSP_ST_GOT_SENSE 0x2000
75
76/* isp_stat_flags field */
77#define FCP_RSP_STAT_DISC 0x0001
78#define FCP_RSP_STAT_SYNC 0x0002
79#define FCP_RSP_STAT_PERR 0x0004
80#define FCP_RSP_STAT_BUS_RESET 0x0008
81#define FCP_RSP_STAT_DEV_RESET 0x0010
82#define FCP_RSP_STAT_ABORTED 0x0020
83#define FCP_RSP_STAT_TIMEOUT 0x0040
84#define FCP_RSP_STAT_NEGOTIATE 0x0080
85
86typedef struct {
87 u8 rsp_info_type;
88 u8 xxx;
89 u16 isp_status;
90 u16 isp_state_flags;
91 u16 isp_stat_flags;
92} fcp_scsi_err;
93
94#endif /* !(__FCP_H) */
diff --git a/drivers/fc4/fcp_impl.h b/drivers/fc4/fcp_impl.h
new file mode 100644
index 000000000000..e44d652a83dc
--- /dev/null
+++ b/drivers/fc4/fcp_impl.h
@@ -0,0 +1,164 @@
1/* fcp_impl.h: Generic SCSI on top of FC4 - our interface defines.
2 *
3 * Copyright (C) 1997-1999 Jakub Jelinek (jj@ultra.linux.cz)
4 * Copyright (C) 1998 Jirka Hanika (geo@ff.cuni.cz)
5 */
6
7#ifndef _FCP_SCSI_H
8#define _FCP_SCSI_H
9
10#include <linux/types.h>
11#include "../scsi/scsi.h"
12
13#include "fc.h"
14#include "fcp.h"
15#include "fc-al.h"
16
17#include <asm/io.h>
18#ifdef __sparc__
19#include <asm/sbus.h>
20#endif
21
22/* 0 or 1 */
23#define FCP_SCSI_USE_NEW_EH_CODE 0
24
25#define FC_CLASS_OUTBOUND 0x01
26#define FC_CLASS_INBOUND 0x02
27#define FC_CLASS_SIMPLE 0x03
28#define FC_CLASS_IO_WRITE 0x04
29#define FC_CLASS_IO_READ 0x05
30#define FC_CLASS_UNSOLICITED 0x06
31#define FC_CLASS_OFFLINE 0x08
32
33#define PROTO_OFFLINE 0x02
34#define PROTO_REPORT_AL_MAP 0x03
35#define PROTO_FORCE_LIP 0x06
36
37struct _fc_channel;
38
39typedef struct fcp_cmnd {
40 struct fcp_cmnd *next;
41 struct fcp_cmnd *prev;
42 void (*done)(Scsi_Cmnd *);
43 unsigned short proto;
44 unsigned short token;
45 unsigned int did;
46 /* FCP SCSI stuff */
47 dma_addr_t data;
48 /* From now on this cannot be touched for proto == TYPE_SCSI_FCP */
49 fc_hdr fch;
50 dma_addr_t cmd;
51 dma_addr_t rsp;
52 int cmdlen;
53 int rsplen;
54 int class;
55 int datalen;
56 /* This is just used as a verification during login */
57 struct _fc_channel *fc;
58 void *ls;
59} fcp_cmnd;
60
61typedef struct {
62 unsigned int len;
63 unsigned char list[0];
64} fcp_posmap;
65
66typedef struct _fc_channel {
67 struct _fc_channel *next;
68 int irq;
69 int state;
70 int sid;
71 int did;
72 char name[16];
73 void (*fcp_register)(struct _fc_channel *, u8, int);
74 void (*reset)(struct _fc_channel *);
75 int (*hw_enque)(struct _fc_channel *, fcp_cmnd *);
76 fc_wwn wwn_node;
77 fc_wwn wwn_nport;
78 fc_wwn wwn_dest;
79 common_svc_parm *common_svc;
80 svc_parm *class_svcs;
81#ifdef __sparc__
82 struct sbus_dev *dev;
83#else
84 struct pci_dev *dev;
85#endif
86 struct module *module;
87 /* FCP SCSI stuff */
88 short can_queue;
89 short abort_count;
90 int rsp_size;
91 fcp_cmd *scsi_cmd_pool;
92 char *scsi_rsp_pool;
93 dma_addr_t dma_scsi_cmd, dma_scsi_rsp;
94 long *scsi_bitmap;
95 long scsi_bitmap_end;
96 int scsi_free;
97 int (*encode_addr)(Scsi_Cmnd *, u16 *, struct _fc_channel *, fcp_cmnd *);
98 fcp_cmnd *scsi_que;
99 char scsi_name[4];
100 fcp_cmnd **cmd_slots;
101 int channels;
102 int targets;
103 long *ages;
104 Scsi_Cmnd *rst_pkt;
105 fcp_posmap *posmap;
106 /* LOGIN stuff */
107 fcp_cmnd *login;
108 void *ls;
109} fc_channel;
110
111extern fc_channel *fc_channels;
112
113#define FC_STATE_UNINITED 0
114#define FC_STATE_ONLINE 1
115#define FC_STATE_OFFLINE 2
116#define FC_STATE_RESETING 3
117#define FC_STATE_FPORT_OK 4
118#define FC_STATE_MAYBEOFFLINE 5
119
120#define FC_STATUS_OK 0
121#define FC_STATUS_P_RJT 2
122#define FC_STATUS_F_RJT 3
123#define FC_STATUS_P_BSY 4
124#define FC_STATUS_F_BSY 5
125#define FC_STATUS_ERR_OFFLINE 0x11
126#define FC_STATUS_TIMEOUT 0x12
127#define FC_STATUS_ERR_OVERRUN 0x13
128#define FC_STATUS_POINTTOPOINT 0x15
129#define FC_STATUS_AL 0x16
130#define FC_STATUS_UNKNOWN_CQ_TYPE 0x20
131#define FC_STATUS_BAD_SEG_CNT 0x21
132#define FC_STATUS_MAX_XCHG_EXCEEDED 0x22
133#define FC_STATUS_BAD_XID 0x23
134#define FC_STATUS_XCHG_BUSY 0x24
135#define FC_STATUS_BAD_POOL_ID 0x25
136#define FC_STATUS_INSUFFICIENT_CQES 0x26
137#define FC_STATUS_ALLOC_FAIL 0x27
138#define FC_STATUS_BAD_SID 0x28
139#define FC_STATUS_NO_SEQ_INIT 0x29
140#define FC_STATUS_TIMED_OUT -1
141#define FC_STATUS_BAD_RSP -2
142
143void fcp_queue_empty(fc_channel *);
144int fcp_init(fc_channel *);
145void fcp_release(fc_channel *fc_chain, int count);
146void fcp_receive_solicited(fc_channel *, int, int, int, fc_hdr *);
147void fcp_state_change(fc_channel *, int);
148int fc_do_plogi(fc_channel *, unsigned char, fc_wwn *, fc_wwn *);
149int fc_do_prli(fc_channel *, unsigned char);
150
151#define for_each_fc_channel(fc) \
152 for (fc = fc_channels; fc; fc = fc->next)
153
154#define for_each_online_fc_channel(fc) \
155 for_each_fc_channel(fc) \
156 if (fc->state == FC_STATE_ONLINE)
157
158int fcp_scsi_queuecommand(Scsi_Cmnd *, void (* done)(Scsi_Cmnd *));
159int fcp_scsi_abort(Scsi_Cmnd *);
160int fcp_scsi_dev_reset(Scsi_Cmnd *);
161int fcp_scsi_bus_reset(Scsi_Cmnd *);
162int fcp_scsi_host_reset(Scsi_Cmnd *);
163
164#endif /* !(_FCP_SCSI_H) */
diff --git a/drivers/fc4/soc.c b/drivers/fc4/soc.c
new file mode 100644
index 000000000000..247b46302777
--- /dev/null
+++ b/drivers/fc4/soc.c
@@ -0,0 +1,766 @@
1/* soc.c: Sparc SUNW,soc (Serial Optical Channel) Fibre Channel Sbus adapter support.
2 *
3 * Copyright (C) 1996,1997,1999 Jakub Jelinek (jj@ultra.linux.cz)
4 * Copyright (C) 1997,1998 Jirka Hanika (geo@ff.cuni.cz)
5 *
6 * Sources:
7 * Fibre Channel Physical & Signaling Interface (FC-PH), dpANS, 1994
8 * dpANS Fibre Channel Protocol for SCSI (X3.269-199X), Rev. 012, 1995
9 *
10 * Supported hardware:
11 * Tested on SOC sbus card bought with SS1000 in Linux running on SS5 and Ultra1.
12 * For SOC sbus cards, you have to make sure your FCode is 1.52 or later.
13 * If you have older FCode, you should try to upgrade or get SOC microcode from Sun
14 * (the microcode is present in Solaris soc driver as well). In that case you need
15 * to #define HAVE_SOC_UCODE and format the microcode into soc_asm.c. For the exact
16 * format mail me and I will tell you. I cannot offer you the actual microcode though,
17 * unless Sun confirms they don't mind.
18 */
19
20static char *version =
21 "soc.c:v1.3 9/Feb/99 Jakub Jelinek (jj@ultra.linux.cz), Jirka Hanika (geo@ff.cuni.cz)\n";
22
23#include <linux/module.h>
24#include <linux/kernel.h>
25#include <linux/sched.h>
26#include <linux/types.h>
27#include <linux/fcntl.h>
28#include <linux/interrupt.h>
29#include <linux/ptrace.h>
30#include <linux/ioport.h>
31#include <linux/in.h>
32#include <linux/slab.h>
33#include <linux/string.h>
34#include <linux/init.h>
35#include <linux/bitops.h>
36#include <asm/io.h>
37#include <asm/dma.h>
38#include <linux/errno.h>
39#include <asm/byteorder.h>
40
41#include <asm/openprom.h>
42#include <asm/oplib.h>
43#include <asm/pgtable.h>
44#include <asm/irq.h>
45
46/* #define SOCDEBUG */
47/* #define HAVE_SOC_UCODE */
48
49#include "fcp_impl.h"
50#include "soc.h"
51#ifdef HAVE_SOC_UCODE
52#include "soc_asm.h"
53#endif
54
55#define soc_printk printk ("soc%d: ", s->soc_no); printk
56
57#ifdef SOCDEBUG
58#define SOD(x) soc_printk x;
59#else
60#define SOD(x)
61#endif
62
63#define for_each_soc(s) for (s = socs; s; s = s->next)
64struct soc *socs = NULL;
65
66static inline void soc_disable(struct soc *s)
67{
68 sbus_writel(0, s->regs + IMASK);
69 sbus_writel(SOC_CMD_SOFT_RESET, s->regs + CMD);
70}
71
72static inline void soc_enable(struct soc *s)
73{
74 SOD(("enable %08x\n", s->cfg))
75 sbus_writel(0, s->regs + SAE);
76 sbus_writel(s->cfg, s->regs + CFG);
77 sbus_writel(SOC_CMD_RSP_QALL, s->regs + CMD);
78 SOC_SETIMASK(s, SOC_IMASK_RSP_QALL | SOC_IMASK_SAE);
79 SOD(("imask %08lx %08lx\n", s->imask, sbus_readl(s->regs + IMAK)));
80}
81
82static void soc_reset(fc_channel *fc)
83{
84 soc_port *port = (soc_port *)fc;
85 struct soc *s = port->s;
86
87 /* FIXME */
88 soc_disable(s);
89 s->req[0].seqno = 1;
90 s->req[1].seqno = 1;
91 s->rsp[0].seqno = 1;
92 s->rsp[1].seqno = 1;
93 s->req[0].in = 0;
94 s->req[1].in = 0;
95 s->rsp[0].in = 0;
96 s->rsp[1].in = 0;
97 s->req[0].out = 0;
98 s->req[1].out = 0;
99 s->rsp[0].out = 0;
100 s->rsp[1].out = 0;
101
102 /* FIXME */
103 soc_enable(s);
104}
105
106static inline void soc_solicited (struct soc *s)
107{
108 fc_hdr fchdr;
109 soc_rsp __iomem *hwrsp;
110 soc_cq_rsp *sw_cq;
111 int token;
112 int status;
113 fc_channel *fc;
114
115 sw_cq = &s->rsp[SOC_SOLICITED_RSP_Q];
116
117 if (sw_cq->pool == NULL)
118 sw_cq->pool = (soc_req __iomem *)
119 (s->xram + xram_get_32low ((xram_p)&sw_cq->hw_cq->address));
120 sw_cq->in = xram_get_8 ((xram_p)&sw_cq->hw_cq->in);
121 SOD (("soc_solicited, %d pkts arrived\n", (sw_cq->in-sw_cq->out) & sw_cq->last))
122 for (;;) {
123 hwrsp = (soc_rsp __iomem *)sw_cq->pool + sw_cq->out;
124 token = xram_get_32low ((xram_p)&hwrsp->shdr.token);
125 status = xram_get_32low ((xram_p)&hwrsp->status);
126 fc = (fc_channel *)(&s->port[(token >> 11) & 1]);
127
128 if (status == SOC_OK) {
129 fcp_receive_solicited(fc, token >> 12,
130 token & ((1 << 11) - 1),
131 FC_STATUS_OK, NULL);
132 } else {
133 xram_copy_from(&fchdr, (xram_p)&hwrsp->fchdr, sizeof(fchdr));
134 /* We have intentionally defined FC_STATUS_* constants
135 * to match SOC_* constants, otherwise we'd have to
136 * translate status.
137 */
138 fcp_receive_solicited(fc, token >> 12,
139 token & ((1 << 11) - 1),
140 status, &fchdr);
141 }
142
143 if (++sw_cq->out > sw_cq->last) {
144 sw_cq->seqno++;
145 sw_cq->out = 0;
146 }
147
148 if (sw_cq->out == sw_cq->in) {
149 sw_cq->in = xram_get_8 ((xram_p)&sw_cq->hw_cq->in);
150 if (sw_cq->out == sw_cq->in) {
151 /* Tell the hardware about it */
152 sbus_writel((sw_cq->out << 24) |
153 (SOC_CMD_RSP_QALL &
154 ~(SOC_CMD_RSP_Q0 << SOC_SOLICITED_RSP_Q)),
155 s->regs + CMD);
156
157 /* Read it, so that we're sure it has been updated */
158 sbus_readl(s->regs + CMD);
159 sw_cq->in = xram_get_8 ((xram_p)&sw_cq->hw_cq->in);
160 if (sw_cq->out == sw_cq->in)
161 break;
162 }
163 }
164 }
165}
166
167static inline void soc_request (struct soc *s, u32 cmd)
168{
169 SOC_SETIMASK(s, s->imask & ~(cmd & SOC_CMD_REQ_QALL));
170 SOD(("imask %08lx %08lx\n", s->imask, sbus_readl(s->regs + IMASK)));
171
172 SOD(("Queues available %08x OUT %X %X\n", cmd,
173 xram_get_8((xram_p)&s->req[0].hw_cq->out),
174 xram_get_8((xram_p)&s->req[0].hw_cq->out)))
175 if (s->port[s->curr_port].fc.state != FC_STATE_OFFLINE) {
176 fcp_queue_empty ((fc_channel *)&(s->port[s->curr_port]));
177 if (((s->req[1].in + 1) & s->req[1].last) != (s->req[1].out))
178 fcp_queue_empty ((fc_channel *)&(s->port[1 - s->curr_port]));
179 } else {
180 fcp_queue_empty ((fc_channel *)&(s->port[1 - s->curr_port]));
181 }
182 if (s->port[1 - s->curr_port].fc.state != FC_STATE_OFFLINE)
183 s->curr_port ^= 1;
184}
185
186static inline void soc_unsolicited (struct soc *s)
187{
188 soc_rsp __iomem *hwrsp, *hwrspc;
189 soc_cq_rsp *sw_cq;
190 int count;
191 int status;
192 int flags;
193 fc_channel *fc;
194
195 sw_cq = &s->rsp[SOC_UNSOLICITED_RSP_Q];
196 if (sw_cq->pool == NULL)
197 sw_cq->pool = (soc_req __iomem *)
198 (s->xram + (xram_get_32low ((xram_p)&sw_cq->hw_cq->address)));
199
200 sw_cq->in = xram_get_8 ((xram_p)&sw_cq->hw_cq->in);
201 SOD (("soc_unsolicited, %d packets arrived\n", (sw_cq->in - sw_cq->out) & sw_cq->last))
202 while (sw_cq->in != sw_cq->out) {
203 /* ...real work per entry here... */
204 hwrsp = (soc_rsp __iomem *)sw_cq->pool + sw_cq->out;
205
206 hwrspc = NULL;
207 flags = xram_get_16 ((xram_p)&hwrsp->shdr.flags);
208 count = xram_get_8 ((xram_p)&hwrsp->count);
209 fc = (fc_channel *)&s->port[flags & SOC_PORT_B];
210 SOD(("FC %08lx fcp_state_change %08lx\n",
211 (long)fc, (long)fc->fcp_state_change))
212
213 if (count != 1) {
214 /* Ugh, continuation entries */
215 u8 in;
216
217 if (count != 2) {
218 printk("%s: Too many continuations entries %d\n",
219 fc->name, count);
220 goto update_out;
221 }
222
223 in = sw_cq->in;
224 if (in < sw_cq->out) in += sw_cq->last + 1;
225 if (in < sw_cq->out + 2) {
226 /* Ask the hardware if they haven't arrived yet. */
227 sbus_writel((sw_cq->out << 24) |
228 (SOC_CMD_RSP_QALL &
229 ~(SOC_CMD_RSP_Q0 << SOC_UNSOLICITED_RSP_Q)),
230 s->regs + CMD);
231
232 /* Read it, so that we're sure it has been updated */
233 sbus_readl(s->regs + CMD);
234 sw_cq->in = xram_get_8 ((xram_p)&sw_cq->hw_cq->in);
235 in = sw_cq->in;
236 if (in < sw_cq->out)
237 in += sw_cq->last + 1;
238 if (in < sw_cq->out + 2) /* Nothing came, let us wait */
239 return;
240 }
241 if (sw_cq->out == sw_cq->last)
242 hwrspc = (soc_rsp __iomem *)sw_cq->pool;
243 else
244 hwrspc = hwrsp + 1;
245 }
246
247 switch (flags & ~SOC_PORT_B) {
248 case SOC_STATUS:
249 status = xram_get_32low ((xram_p)&hwrsp->status);
250 switch (status) {
251 case SOC_ONLINE:
252 SOD(("State change to ONLINE\n"));
253 fcp_state_change(fc, FC_STATE_ONLINE);
254 break;
255 case SOC_OFFLINE:
256 SOD(("State change to OFFLINE\n"));
257 fcp_state_change(fc, FC_STATE_OFFLINE);
258 break;
259 default:
260 printk ("%s: Unknown STATUS no %d\n",
261 fc->name, status);
262 break;
263 }
264 break;
265 case (SOC_UNSOLICITED|SOC_FC_HDR):
266 {
267 int r_ctl = xram_get_8 ((xram_p)&hwrsp->fchdr);
268 unsigned len;
269 char buf[64];
270
271 if ((r_ctl & 0xf0) == R_CTL_EXTENDED_SVC) {
272 len = xram_get_32 ((xram_p)&hwrsp->shdr.bytecnt);
273 if (len < 4 || !hwrspc) {
274 printk ("%s: Invalid R_CTL %02x "
275 "continuation entries\n",
276 fc->name, r_ctl);
277 } else {
278 if (len > 60)
279 len = 60;
280 xram_copy_from (buf, (xram_p)hwrspc,
281 (len + 3) & ~3);
282 if (*(u32 *)buf == LS_DISPLAY) {
283 int i;
284
285 for (i = 4; i < len; i++)
286 if (buf[i] == '\n')
287 buf[i] = ' ';
288 buf[len] = 0;
289 printk ("%s message: %s\n",
290 fc->name, buf + 4);
291 } else {
292 printk ("%s: Unknown LS_CMD "
293 "%02x\n", fc->name,
294 buf[0]);
295 }
296 }
297 } else {
298 printk ("%s: Unsolicited R_CTL %02x "
299 "not handled\n", fc->name, r_ctl);
300 }
301 }
302 break;
303 default:
304 printk ("%s: Unexpected flags %08x\n", fc->name, flags);
305 break;
306 };
307update_out:
308 if (++sw_cq->out > sw_cq->last) {
309 sw_cq->seqno++;
310 sw_cq->out = 0;
311 }
312
313 if (hwrspc) {
314 if (++sw_cq->out > sw_cq->last) {
315 sw_cq->seqno++;
316 sw_cq->out = 0;
317 }
318 }
319
320 if (sw_cq->out == sw_cq->in) {
321 sw_cq->in = xram_get_8 ((xram_p)&sw_cq->hw_cq->in);
322 if (sw_cq->out == sw_cq->in) {
323 /* Tell the hardware about it */
324 sbus_writel((sw_cq->out << 24) |
325 (SOC_CMD_RSP_QALL &
326 ~(SOC_CMD_RSP_Q0 << SOC_UNSOLICITED_RSP_Q)),
327 s->regs + CMD);
328
329 /* Read it, so that we're sure it has been updated */
330 sbus_readl(s->regs + CMD);
331 sw_cq->in = xram_get_8 ((xram_p)&sw_cq->hw_cq->in);
332 }
333 }
334 }
335}
336
337static irqreturn_t soc_intr(int irq, void *dev_id, struct pt_regs *regs)
338{
339 u32 cmd;
340 unsigned long flags;
341 register struct soc *s = (struct soc *)dev_id;
342
343 spin_lock_irqsave(&s->lock, flags);
344 cmd = sbus_readl(s->regs + CMD);
345 for (; (cmd = SOC_INTR (s, cmd)); cmd = sbus_readl(s->regs + CMD)) {
346 if (cmd & SOC_CMD_RSP_Q1) soc_unsolicited (s);
347 if (cmd & SOC_CMD_RSP_Q0) soc_solicited (s);
348 if (cmd & SOC_CMD_REQ_QALL) soc_request (s, cmd);
349 }
350 spin_unlock_irqrestore(&s->lock, flags);
351
352 return IRQ_HANDLED;
353}
354
355#define TOKEN(proto, port, token) (((proto)<<12)|(token)|(port))
356
357static int soc_hw_enque (fc_channel *fc, fcp_cmnd *fcmd)
358{
359 soc_port *port = (soc_port *)fc;
360 struct soc *s = port->s;
361 int qno;
362 soc_cq_req *sw_cq;
363 int cq_next_in;
364 soc_req *request;
365 fc_hdr *fch;
366 int i;
367
368 if (fcmd->proto == TYPE_SCSI_FCP)
369 qno = 1;
370 else
371 qno = 0;
372 SOD(("Putting a FCP packet type %d into hw queue %d\n", fcmd->proto, qno))
373 if (s->imask & (SOC_IMASK_REQ_Q0 << qno)) {
374 SOD(("EIO %08x\n", s->imask))
375 return -EIO;
376 }
377 sw_cq = s->req + qno;
378 cq_next_in = (sw_cq->in + 1) & sw_cq->last;
379
380 if (cq_next_in == sw_cq->out &&
381 cq_next_in == (sw_cq->out = xram_get_8((xram_p)&sw_cq->hw_cq->out))) {
382 SOD(("%d IN %d OUT %d LAST %d\n", qno, sw_cq->in, sw_cq->out, sw_cq->last))
383 SOC_SETIMASK(s, s->imask | (SOC_IMASK_REQ_Q0 << qno));
384 SOD(("imask %08lx %08lx\n", s->imask, sbus_readl(s->regs + IMASK)));
385 /* If queue is full, just say NO */
386 return -EBUSY;
387 }
388
389 request = sw_cq->pool + sw_cq->in;
390 fch = &request->fchdr;
391
392 switch (fcmd->proto) {
393 case TYPE_SCSI_FCP:
394 request->shdr.token = TOKEN(TYPE_SCSI_FCP, port->mask, fcmd->token);
395 request->data[0].base = fc->dma_scsi_cmd + fcmd->token * sizeof(fcp_cmd);
396 request->data[0].count = sizeof(fcp_cmd);
397 request->data[1].base = fc->dma_scsi_rsp + fcmd->token * fc->rsp_size;
398 request->data[1].count = fc->rsp_size;
399 if (fcmd->data) {
400 request->shdr.segcnt = 3;
401 i = fc->scsi_cmd_pool[fcmd->token].fcp_data_len;
402 request->shdr.bytecnt = i;
403 request->data[2].base = fcmd->data;
404 request->data[2].count = i;
405 request->type =
406 (fc->scsi_cmd_pool[fcmd->token].fcp_cntl & FCP_CNTL_WRITE) ?
407 SOC_CQTYPE_IO_WRITE : SOC_CQTYPE_IO_READ;
408 } else {
409 request->shdr.segcnt = 2;
410 request->shdr.bytecnt = 0;
411 request->data[2].base = 0;
412 request->data[2].count = 0;
413 request->type = SOC_CQTYPE_SIMPLE;
414 }
415 FILL_FCHDR_RCTL_DID(fch, R_CTL_COMMAND, fc->did);
416 FILL_FCHDR_SID(fch, fc->sid);
417 FILL_FCHDR_TYPE_FCTL(fch, TYPE_SCSI_FCP,
418 F_CTL_FIRST_SEQ | F_CTL_SEQ_INITIATIVE);
419 FILL_FCHDR_SEQ_DF_SEQ(fch, 0, 0, 0);
420 FILL_FCHDR_OXRX(fch, 0xffff, 0xffff);
421 fch->param = 0;
422 request->shdr.flags = port->flags;
423 request->shdr.class = 2;
424 break;
425
426 case PROTO_OFFLINE:
427 memset (request, 0, sizeof(*request));
428 request->shdr.token = TOKEN(PROTO_OFFLINE, port->mask, fcmd->token);
429 request->type = SOC_CQTYPE_OFFLINE;
430 FILL_FCHDR_RCTL_DID(fch, R_CTL_COMMAND, fc->did);
431 FILL_FCHDR_SID(fch, fc->sid);
432 FILL_FCHDR_TYPE_FCTL(fch, TYPE_SCSI_FCP,
433 F_CTL_FIRST_SEQ | F_CTL_SEQ_INITIATIVE);
434 FILL_FCHDR_SEQ_DF_SEQ(fch, 0, 0, 0);
435 FILL_FCHDR_OXRX(fch, 0xffff, 0xffff);
436 request->shdr.flags = port->flags;
437 break;
438
439 case PROTO_REPORT_AL_MAP:
440 /* SOC only supports Point-to-Point topology, no FC-AL, sorry... */
441 return -ENOSYS;
442
443 default:
444 request->shdr.token = TOKEN(fcmd->proto, port->mask, fcmd->token);
445 request->shdr.class = 2;
446 request->shdr.flags = port->flags;
447 memcpy (fch, &fcmd->fch, sizeof(fc_hdr));
448 request->data[0].count = fcmd->cmdlen;
449 request->data[1].count = fcmd->rsplen;
450 request->type = fcmd->class;
451 switch (fcmd->class) {
452 case FC_CLASS_OUTBOUND:
453 request->data[0].base = fcmd->cmd;
454 request->data[0].count = fcmd->cmdlen;
455 request->type = SOC_CQTYPE_OUTBOUND;
456 request->shdr.bytecnt = fcmd->cmdlen;
457 request->shdr.segcnt = 1;
458 break;
459 case FC_CLASS_INBOUND:
460 request->data[0].base = fcmd->rsp;
461 request->data[0].count = fcmd->rsplen;
462 request->type = SOC_CQTYPE_INBOUND;
463 request->shdr.bytecnt = 0;
464 request->shdr.segcnt = 1;
465 break;
466 case FC_CLASS_SIMPLE:
467 request->data[0].base = fcmd->cmd;
468 request->data[1].base = fcmd->rsp;
469 request->data[0].count = fcmd->cmdlen;
470 request->data[1].count = fcmd->rsplen;
471 request->type = SOC_CQTYPE_SIMPLE;
472 request->shdr.bytecnt = fcmd->cmdlen;
473 request->shdr.segcnt = 2;
474 break;
475 case FC_CLASS_IO_READ:
476 case FC_CLASS_IO_WRITE:
477 request->data[0].base = fcmd->cmd;
478 request->data[1].base = fcmd->rsp;
479 request->data[0].count = fcmd->cmdlen;
480 request->data[1].count = fcmd->rsplen;
481 request->type =
482 (fcmd->class == FC_CLASS_IO_READ) ?
483 SOC_CQTYPE_IO_READ : SOC_CQTYPE_IO_WRITE;
484 if (fcmd->data) {
485 request->data[2].base = fcmd->data;
486 request->data[2].count = fcmd->datalen;
487 request->shdr.bytecnt = fcmd->datalen;
488 request->shdr.segcnt = 3;
489 } else {
490 request->shdr.bytecnt = 0;
491 request->shdr.segcnt = 2;
492 }
493 break;
494 };
495 break;
496 };
497
498 request->count = 1;
499 request->flags = 0;
500 request->seqno = sw_cq->seqno;
501
502 /* And now tell the SOC about it */
503
504 if (++sw_cq->in > sw_cq->last) {
505 sw_cq->in = 0;
506 sw_cq->seqno++;
507 }
508
509 SOD(("Putting %08x into cmd\n",
510 SOC_CMD_RSP_QALL | (sw_cq->in << 24) | (SOC_CMD_REQ_Q0 << qno)))
511
512 sbus_writel(SOC_CMD_RSP_QALL | (sw_cq->in << 24) | (SOC_CMD_REQ_Q0 << qno),
513 s->regs + CMD);
514
515 /* Read so that command is completed. */
516 sbus_readl(s->regs + CMD);
517
518 return 0;
519}
520
521static inline void soc_download_fw(struct soc *s)
522{
523#ifdef HAVE_SOC_UCODE
524 xram_copy_to (s->xram, soc_ucode, sizeof(soc_ucode));
525 xram_bzero (s->xram + sizeof(soc_ucode), 32768 - sizeof(soc_ucode));
526#endif
527}
528
529/* Check for what the best SBUS burst we can use happens
530 * to be on this machine.
531 */
532static inline void soc_init_bursts(struct soc *s, struct sbus_dev *sdev)
533{
534 int bsizes, bsizes_more;
535
536 bsizes = (prom_getintdefault(sdev->prom_node,"burst-sizes",0xff) & 0xff);
537 bsizes_more = (prom_getintdefault(sdev->bus->prom_node, "burst-sizes", 0xff) & 0xff);
538 bsizes &= bsizes_more;
539 if ((bsizes & 0x7f) == 0x7f)
540 s->cfg = SOC_CFG_BURST_64;
541 else if ((bsizes & 0x3f) == 0x3f)
542 s->cfg = SOC_CFG_BURST_32;
543 else if ((bsizes & 0x1f) == 0x1f)
544 s->cfg = SOC_CFG_BURST_16;
545 else
546 s->cfg = SOC_CFG_BURST_4;
547}
548
549static inline void soc_init(struct sbus_dev *sdev, int no)
550{
551 unsigned char tmp[60];
552 int propl;
553 struct soc *s;
554 static int version_printed = 0;
555 soc_hw_cq cq[8];
556 int size, i;
557 int irq;
558
559 s = kmalloc (sizeof (struct soc), GFP_KERNEL);
560 if (s == NULL)
561 return;
562 memset (s, 0, sizeof(struct soc));
563 spin_lock_init(&s->lock);
564 s->soc_no = no;
565
566 SOD(("socs %08lx soc_intr %08lx soc_hw_enque %08x\n",
567 (long)socs, (long)soc_intr, (long)soc_hw_enque))
568 if (version_printed++ == 0)
569 printk (version);
570
571 s->port[0].fc.module = THIS_MODULE;
572 s->port[1].fc.module = THIS_MODULE;
573
574 s->next = socs;
575 socs = s;
576 s->port[0].fc.dev = sdev;
577 s->port[1].fc.dev = sdev;
578 s->port[0].s = s;
579 s->port[1].s = s;
580
581 s->port[0].fc.next = &s->port[1].fc;
582
583 /* World Wide Name of SOC */
584 propl = prom_getproperty (sdev->prom_node, "soc-wwn", tmp, sizeof(tmp));
585 if (propl != sizeof (fc_wwn)) {
586 s->wwn.naaid = NAAID_IEEE;
587 s->wwn.lo = 0x12345678;
588 } else
589 memcpy (&s->wwn, tmp, sizeof (fc_wwn));
590
591 propl = prom_getproperty (sdev->prom_node, "port-wwns", tmp, sizeof(tmp));
592 if (propl != 2 * sizeof (fc_wwn)) {
593 s->port[0].fc.wwn_nport.naaid = NAAID_IEEE_EXT;
594 s->port[0].fc.wwn_nport.hi = s->wwn.hi;
595 s->port[0].fc.wwn_nport.lo = s->wwn.lo;
596 s->port[1].fc.wwn_nport.naaid = NAAID_IEEE_EXT;
597 s->port[1].fc.wwn_nport.nportid = 1;
598 s->port[1].fc.wwn_nport.hi = s->wwn.hi;
599 s->port[1].fc.wwn_nport.lo = s->wwn.lo;
600 } else {
601 memcpy (&s->port[0].fc.wwn_nport, tmp, sizeof (fc_wwn));
602 memcpy (&s->port[1].fc.wwn_nport, tmp + sizeof (fc_wwn), sizeof (fc_wwn));
603 }
604 memcpy (&s->port[0].fc.wwn_node, &s->wwn, sizeof (fc_wwn));
605 memcpy (&s->port[1].fc.wwn_node, &s->wwn, sizeof (fc_wwn));
606 SOD(("Got wwns %08x%08x ports %08x%08x and %08x%08x\n",
607 *(u32 *)&s->port[0].fc.wwn_nport, s->port[0].fc.wwn_nport.lo,
608 *(u32 *)&s->port[0].fc.wwn_nport, s->port[0].fc.wwn_nport.lo,
609 *(u32 *)&s->port[1].fc.wwn_nport, s->port[1].fc.wwn_nport.lo))
610
611 s->port[0].fc.sid = 1;
612 s->port[1].fc.sid = 17;
613 s->port[0].fc.did = 2;
614 s->port[1].fc.did = 18;
615
616 s->port[0].fc.reset = soc_reset;
617 s->port[1].fc.reset = soc_reset;
618
619 if (sdev->num_registers == 1) {
620 /* Probably SunFire onboard SOC */
621 s->xram = sbus_ioremap(&sdev->resource[0], 0,
622 0x10000UL, "soc xram");
623 s->regs = sbus_ioremap(&sdev->resource[0], 0x10000UL,
624 0x10UL, "soc regs");
625 } else {
626 /* Probably SOC sbus card */
627 s->xram = sbus_ioremap(&sdev->resource[1], 0,
628 sdev->reg_addrs[1].reg_size, "soc xram");
629 s->regs = sbus_ioremap(&sdev->resource[2], 0,
630 sdev->reg_addrs[2].reg_size, "soc regs");
631 }
632
633 soc_init_bursts(s, sdev);
634
635 SOD(("Disabling SOC\n"))
636
637 soc_disable (s);
638
639 irq = sdev->irqs[0];
640
641 if (request_irq (irq, soc_intr, SA_SHIRQ, "SOC", (void *)s)) {
642 soc_printk ("Cannot order irq %d to go\n", irq);
643 socs = s->next;
644 return;
645 }
646
647 SOD(("SOC uses IRQ%s\n", __irq_itoa(irq)))
648
649 s->port[0].fc.irq = irq;
650 s->port[1].fc.irq = irq;
651
652 sprintf (s->port[0].fc.name, "soc%d port A", no);
653 sprintf (s->port[1].fc.name, "soc%d port B", no);
654 s->port[0].flags = SOC_FC_HDR | SOC_PORT_A;
655 s->port[1].flags = SOC_FC_HDR | SOC_PORT_B;
656 s->port[1].mask = (1 << 11);
657
658 s->port[0].fc.hw_enque = soc_hw_enque;
659 s->port[1].fc.hw_enque = soc_hw_enque;
660
661 soc_download_fw (s);
662
663 SOD(("Downloaded firmware\n"))
664
665 /* Now setup xram circular queues */
666 memset (cq, 0, sizeof(cq));
667
668 size = (SOC_CQ_REQ0_SIZE + SOC_CQ_REQ1_SIZE) * sizeof(soc_req);
669 s->req_cpu = sbus_alloc_consistent(sdev, size, &s->req_dvma);
670 s->req[0].pool = s->req_cpu;
671 cq[0].address = s->req_dvma;
672 s->req[1].pool = s->req[0].pool + SOC_CQ_REQ0_SIZE;
673
674 s->req[0].hw_cq = (soc_hw_cq __iomem *)(s->xram + SOC_CQ_REQ_OFFSET);
675 s->req[1].hw_cq = (soc_hw_cq __iomem *)(s->xram + SOC_CQ_REQ_OFFSET + sizeof(soc_hw_cq));
676 s->rsp[0].hw_cq = (soc_hw_cq __iomem *)(s->xram + SOC_CQ_RSP_OFFSET);
677 s->rsp[1].hw_cq = (soc_hw_cq __iomem *)(s->xram + SOC_CQ_RSP_OFFSET + sizeof(soc_hw_cq));
678
679 cq[1].address = cq[0].address + (SOC_CQ_REQ0_SIZE * sizeof(soc_req));
680 cq[4].address = 1;
681 cq[5].address = 1;
682 cq[0].last = SOC_CQ_REQ0_SIZE - 1;
683 cq[1].last = SOC_CQ_REQ1_SIZE - 1;
684 cq[4].last = SOC_CQ_RSP0_SIZE - 1;
685 cq[5].last = SOC_CQ_RSP1_SIZE - 1;
686 for (i = 0; i < 8; i++)
687 cq[i].seqno = 1;
688
689 s->req[0].last = SOC_CQ_REQ0_SIZE - 1;
690 s->req[1].last = SOC_CQ_REQ1_SIZE - 1;
691 s->rsp[0].last = SOC_CQ_RSP0_SIZE - 1;
692 s->rsp[1].last = SOC_CQ_RSP1_SIZE - 1;
693
694 s->req[0].seqno = 1;
695 s->req[1].seqno = 1;
696 s->rsp[0].seqno = 1;
697 s->rsp[1].seqno = 1;
698
699 xram_copy_to (s->xram + SOC_CQ_REQ_OFFSET, cq, sizeof(cq));
700
701 /* Make our sw copy of SOC service parameters */
702 xram_copy_from (s->serv_params, s->xram + 0x140, sizeof (s->serv_params));
703
704 s->port[0].fc.common_svc = (common_svc_parm *)s->serv_params;
705 s->port[0].fc.class_svcs = (svc_parm *)(s->serv_params + 0x20);
706 s->port[1].fc.common_svc = (common_svc_parm *)&s->serv_params;
707 s->port[1].fc.class_svcs = (svc_parm *)(s->serv_params + 0x20);
708
709 soc_enable (s);
710
711 SOD(("Enabled SOC\n"))
712}
713
714static int __init soc_probe(void)
715{
716 struct sbus_bus *sbus;
717 struct sbus_dev *sdev = NULL;
718 struct soc *s;
719 int cards = 0;
720
721 for_each_sbus(sbus) {
722 for_each_sbusdev(sdev, sbus) {
723 if(!strcmp(sdev->prom_name, "SUNW,soc")) {
724 soc_init(sdev, cards);
725 cards++;
726 }
727 }
728 }
729 if (!cards) return -EIO;
730
731 for_each_soc(s)
732 if (s->next)
733 s->port[1].fc.next = &s->next->port[0].fc;
734 fcp_init (&socs->port[0].fc);
735 return 0;
736}
737
738static void __exit soc_cleanup(void)
739{
740 struct soc *s;
741 int irq;
742 struct sbus_dev *sdev;
743
744 for_each_soc(s) {
745 irq = s->port[0].fc.irq;
746 free_irq (irq, s);
747
748 fcp_release(&(s->port[0].fc), 2);
749
750 sdev = s->port[0].fc.dev;
751 if (sdev->num_registers == 1) {
752 sbus_iounmap(s->xram, 0x10000UL);
753 sbus_iounmap(s->regs, 0x10UL);
754 } else {
755 sbus_iounmap(s->xram, sdev->reg_addrs[1].reg_size);
756 sbus_iounmap(s->regs, sdev->reg_addrs[2].reg_size);
757 }
758 sbus_free_consistent(sdev,
759 (SOC_CQ_REQ0_SIZE+SOC_CQ_REQ1_SIZE)*sizeof(soc_req),
760 s->req_cpu, s->req_dvma);
761 }
762}
763
764module_init(soc_probe);
765module_exit(soc_cleanup);
766MODULE_LICENSE("GPL");
diff --git a/drivers/fc4/soc.h b/drivers/fc4/soc.h
new file mode 100644
index 000000000000..d38cf5b28eed
--- /dev/null
+++ b/drivers/fc4/soc.h
@@ -0,0 +1,301 @@
1/* soc.h: Definitions for Sparc SUNW,soc Fibre Channel Sbus driver.
2 *
3 * Copyright (C) 1996,1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
4 */
5
6#ifndef __SOC_H
7#define __SOC_H
8
9#include "fc.h"
10#include "fcp.h"
11#include "fcp_impl.h"
12
13/* Hardware register offsets and constants first {{{ */
14#define CFG 0x00UL /* Config Register */
15#define SAE 0x04UL /* Slave Access Error Register */
16#define CMD 0x08UL /* Command and Status Register */
17#define IMASK 0x0cUL /* Interrupt Mask Register */
18
19/* Config Register */
20#define SOC_CFG_EXT_RAM_BANK_MASK 0x07000000
21#define SOC_CFG_EEPROM_BANK_MASK 0x00030000
22#define SOC_CFG_BURST64_MASK 0x00000700
23#define SOC_CFG_SBUS_PARITY_TEST 0x00000020
24#define SOC_CFG_SBUS_PARITY_CHECK 0x00000010
25#define SOC_CFG_SBUS_ENHANCED 0x00000008
26#define SOC_CFG_BURST_MASK 0x00000007
27/* Bursts */
28#define SOC_CFG_BURST_4 0x00000000
29#define SOC_CFG_BURST_16 0x00000004
30#define SOC_CFG_BURST_32 0x00000005
31#define SOC_CFG_BURST_64 0x00000006
32
33/* Slave Access Error Register */
34#define SOC_SAE_ALIGNMENT 0x00000004
35#define SOC_SAE_UNSUPPORTED 0x00000002
36#define SOC_SAE_PARITY 0x00000001
37
38/* Command & Status Register */
39#define SOC_CMD_RSP_QALL 0x000f0000
40#define SOC_CMD_RSP_Q0 0x00010000
41#define SOC_CMD_RSP_Q1 0x00020000
42#define SOC_CMD_RSP_Q2 0x00040000
43#define SOC_CMD_RSP_Q3 0x00080000
44#define SOC_CMD_REQ_QALL 0x00000f00
45#define SOC_CMD_REQ_Q0 0x00000100
46#define SOC_CMD_REQ_Q1 0x00000200
47#define SOC_CMD_REQ_Q2 0x00000400
48#define SOC_CMD_REQ_Q3 0x00000800
49#define SOC_CMD_SAE 0x00000080
50#define SOC_CMD_INTR_PENDING 0x00000008
51#define SOC_CMD_NON_QUEUED 0x00000004
52#define SOC_CMD_IDLE 0x00000002
53#define SOC_CMD_SOFT_RESET 0x00000001
54
55/* Interrupt Mask Register */
56#define SOC_IMASK_RSP_QALL 0x000f0000
57#define SOC_IMASK_RSP_Q0 0x00010000
58#define SOC_IMASK_RSP_Q1 0x00020000
59#define SOC_IMASK_RSP_Q2 0x00040000
60#define SOC_IMASK_RSP_Q3 0x00080000
61#define SOC_IMASK_REQ_QALL 0x00000f00
62#define SOC_IMASK_REQ_Q0 0x00000100
63#define SOC_IMASK_REQ_Q1 0x00000200
64#define SOC_IMASK_REQ_Q2 0x00000400
65#define SOC_IMASK_REQ_Q3 0x00000800
66#define SOC_IMASK_SAE 0x00000080
67#define SOC_IMASK_NON_QUEUED 0x00000004
68
69#define SOC_INTR(s, cmd) \
70 (((cmd & SOC_CMD_RSP_QALL) | ((~cmd) & SOC_CMD_REQ_QALL)) \
71 & s->imask)
72
73#define SOC_SETIMASK(s, i) \
74do { (s)->imask = (i); \
75 sbus_writel((i), (s)->regs + IMASK); \
76} while(0)
77
78/* XRAM
79 *
80 * This is a 64KB register area. It accepts only halfword access.
81 * That's why here are the following inline functions...
82 */
83
84typedef void __iomem *xram_p;
85
86/* Get 32bit number from XRAM */
87static inline u32 xram_get_32(xram_p x)
88{
89 return ((sbus_readw(x + 0x00UL) << 16) |
90 (sbus_readw(x + 0x02UL)));
91}
92
93/* Like the above, but when we don't care about the high 16 bits */
94static inline u32 xram_get_32low(xram_p x)
95{
96 return (u32) sbus_readw(x + 0x02UL);
97}
98
99static inline u16 xram_get_16(xram_p x)
100{
101 return sbus_readw(x);
102}
103
104static inline u8 xram_get_8(xram_p x)
105{
106 if ((unsigned long)x & 0x1UL) {
107 x = x - 1;
108 return (u8) sbus_readw(x);
109 } else {
110 return (u8) (sbus_readw(x) >> 8);
111 }
112}
113
114static inline void xram_copy_from(void *p, xram_p x, int len)
115{
116 for (len >>= 2; len > 0; len--, x += sizeof(u32)) {
117 u32 val, *p32 = p;
118
119 val = ((sbus_readw(x + 0x00UL) << 16) |
120 (sbus_readw(x + 0x02UL)));
121 *p32++ = val;
122 p = p32;
123 }
124}
125
126static inline void xram_copy_to(xram_p x, void *p, int len)
127{
128 for (len >>= 2; len > 0; len--, x += sizeof(u32)) {
129 u32 tmp, *p32 = p;
130
131 tmp = *p32++;
132 p = p32;
133 sbus_writew(tmp >> 16, x + 0x00UL);
134 sbus_writew(tmp, x + 0x02UL);
135 }
136}
137
138static inline void xram_bzero(xram_p x, int len)
139{
140 for (len >>= 1; len > 0; len--, x += sizeof(u16))
141 sbus_writew(0, x);
142}
143
144/* Circular Queue */
145
146#define SOC_CQ_REQ_OFFSET (0x100 * sizeof(u16))
147#define SOC_CQ_RSP_OFFSET (0x110 * sizeof(u16))
148
149typedef struct {
150 u32 address;
151 u8 in;
152 u8 out;
153 u8 last;
154 u8 seqno;
155} soc_hw_cq;
156
157#define SOC_PORT_A 0x0000 /* From/To Port A */
158#define SOC_PORT_B 0x0001 /* From/To Port A */
159#define SOC_FC_HDR 0x0002 /* Contains FC Header */
160#define SOC_NORSP 0x0004 /* Don't generate response nor interrupt */
161#define SOC_NOINT 0x0008 /* Generate response but not interrupt */
162#define SOC_XFERRDY 0x0010 /* Generate XFERRDY */
163#define SOC_IGNOREPARAM 0x0020 /* Ignore PARAM field in the FC header */
164#define SOC_COMPLETE 0x0040 /* Command completed */
165#define SOC_UNSOLICITED 0x0080 /* For request this is the packet to establish unsolicited pools, */
166 /* for rsp this is unsolicited packet */
167#define SOC_STATUS 0x0100 /* State change (on/off line) */
168
169typedef struct {
170 u32 token;
171 u16 flags;
172 u8 class;
173 u8 segcnt;
174 u32 bytecnt;
175} soc_hdr;
176
177typedef struct {
178 u32 base;
179 u32 count;
180} soc_data;
181
182#define SOC_CQTYPE_OUTBOUND 0x01
183#define SOC_CQTYPE_INBOUND 0x02
184#define SOC_CQTYPE_SIMPLE 0x03
185#define SOC_CQTYPE_IO_WRITE 0x04
186#define SOC_CQTYPE_IO_READ 0x05
187#define SOC_CQTYPE_UNSOLICITED 0x06
188#define SOC_CQTYPE_DIAG 0x07
189#define SOC_CQTYPE_OFFLINE 0x08
190#define SOC_CQTYPE_RESPONSE 0x10
191#define SOC_CQTYPE_INLINE 0x20
192
193#define SOC_CQFLAGS_CONT 0x01
194#define SOC_CQFLAGS_FULL 0x02
195#define SOC_CQFLAGS_BADHDR 0x04
196#define SOC_CQFLAGS_BADPKT 0x08
197
198typedef struct {
199 soc_hdr shdr;
200 soc_data data[3];
201 fc_hdr fchdr;
202 u8 count;
203 u8 type;
204 u8 flags;
205 u8 seqno;
206} soc_req;
207
208#define SOC_OK 0
209#define SOC_P_RJT 2
210#define SOC_F_RJT 3
211#define SOC_P_BSY 4
212#define SOC_F_BSY 5
213#define SOC_ONLINE 0x10
214#define SOC_OFFLINE 0x11
215#define SOC_TIMEOUT 0x12
216#define SOC_OVERRUN 0x13
217#define SOC_UNKOWN_CQ_TYPE 0x20
218#define SOC_BAD_SEG_CNT 0x21
219#define SOC_MAX_XCHG_EXCEEDED 0x22
220#define SOC_BAD_XID 0x23
221#define SOC_XCHG_BUSY 0x24
222#define SOC_BAD_POOL_ID 0x25
223#define SOC_INSUFFICIENT_CQES 0x26
224#define SOC_ALLOC_FAIL 0x27
225#define SOC_BAD_SID 0x28
226#define SOC_NO_SEG_INIT 0x29
227
228typedef struct {
229 soc_hdr shdr;
230 u32 status;
231 soc_data data;
232 u8 xxx1[12];
233 fc_hdr fchdr;
234 u8 count;
235 u8 type;
236 u8 flags;
237 u8 seqno;
238} soc_rsp;
239
240/* }}} */
241
242/* Now our software structures and constants we use to drive the beast {{{ */
243
244#define SOC_CQ_REQ0_SIZE 4
245#define SOC_CQ_REQ1_SIZE 64
246#define SOC_CQ_RSP0_SIZE 8
247#define SOC_CQ_RSP1_SIZE 4
248
249#define SOC_SOLICITED_RSP_Q 0
250#define SOC_UNSOLICITED_RSP_Q 1
251
252struct soc;
253
254typedef struct {
255 /* This must come first */
256 fc_channel fc;
257 struct soc *s;
258 u16 flags;
259 u16 mask;
260} soc_port;
261
262typedef struct {
263 soc_hw_cq __iomem *hw_cq; /* Related XRAM cq */
264 soc_req __iomem *pool;
265 u8 in;
266 u8 out;
267 u8 last;
268 u8 seqno;
269} soc_cq_rsp;
270
271typedef struct {
272 soc_hw_cq __iomem *hw_cq; /* Related XRAM cq */
273 soc_req *pool;
274 u8 in;
275 u8 out;
276 u8 last;
277 u8 seqno;
278} soc_cq_req;
279
280struct soc {
281 spinlock_t lock;
282 soc_port port[2]; /* Every SOC has one or two FC ports */
283 soc_cq_req req[2]; /* Request CQs */
284 soc_cq_rsp rsp[2]; /* Response CQs */
285 int soc_no;
286 void __iomem *regs;
287 xram_p xram;
288 fc_wwn wwn;
289 u32 imask; /* Our copy of regs->imask */
290 u32 cfg; /* Our copy of regs->cfg */
291 char serv_params[80];
292 struct soc *next;
293 int curr_port; /* Which port will have priority to fcp_queue_empty */
294
295 soc_req *req_cpu;
296 u32 req_dvma;
297};
298
299/* }}} */
300
301#endif /* !(__SOC_H) */
diff --git a/drivers/fc4/socal.c b/drivers/fc4/socal.c
new file mode 100644
index 000000000000..b2377dbd84a1
--- /dev/null
+++ b/drivers/fc4/socal.c
@@ -0,0 +1,906 @@
1/* socal.c: Sparc SUNW,socal (SOC+) Fibre Channel Sbus adapter support.
2 *
3 * Copyright (C) 1998,1999 Jakub Jelinek (jj@ultra.linux.cz)
4 *
5 * Sources:
6 * Fibre Channel Physical & Signaling Interface (FC-PH), dpANS, 1994
7 * dpANS Fibre Channel Protocol for SCSI (X3.269-199X), Rev. 012, 1995
8 * SOC+ Programming Guide 0.1
9 * Fibre Channel Arbitrated Loop (FC-AL), dpANS rev. 4.5, 1995
10 *
11 * Supported hardware:
12 * On-board SOC+ adapters of Ultra Enterprise servers and sun4d.
13 */
14
15static char *version =
16 "socal.c: SOC+ driver v1.1 9/Feb/99 Jakub Jelinek (jj@ultra.linux.cz)\n";
17
18#include <linux/module.h>
19#include <linux/kernel.h>
20#include <linux/sched.h>
21#include <linux/types.h>
22#include <linux/fcntl.h>
23#include <linux/interrupt.h>
24#include <linux/ptrace.h>
25#include <linux/ioport.h>
26#include <linux/in.h>
27#include <linux/slab.h>
28#include <linux/string.h>
29#include <linux/init.h>
30#include <linux/bitops.h>
31#include <asm/system.h>
32#include <asm/io.h>
33#include <asm/dma.h>
34#include <linux/errno.h>
35#include <asm/byteorder.h>
36
37#include <asm/openprom.h>
38#include <asm/oplib.h>
39#include <asm/pgtable.h>
40#include <asm/irq.h>
41
42/* #define SOCALDEBUG */
43/* #define HAVE_SOCAL_UCODE */
44/* #define USE_64BIT_MODE */
45
46#include "fcp_impl.h"
47#include "socal.h"
48#ifdef HAVE_SOCAL_UCODE
49#include "socal_asm.h"
50#endif
51
52#define socal_printk printk ("socal%d: ", s->socal_no); printk
53
54#ifdef SOCALDEBUG
55#define SOD(x) socal_printk x;
56#else
57#define SOD(x)
58#endif
59
60#define for_each_socal(s) for (s = socals; s; s = s->next)
61struct socal *socals = NULL;
62
63static void socal_copy_from_xram(void *d, void __iomem *xram, long size)
64{
65 u32 *dp = (u32 *) d;
66 while (size) {
67 *dp++ = sbus_readl(xram);
68 xram += sizeof(u32);
69 size -= sizeof(u32);
70 }
71}
72
73static void socal_copy_to_xram(void __iomem *xram, void *s, long size)
74{
75 u32 *sp = (u32 *) s;
76 while (size) {
77 u32 val = *sp++;
78 sbus_writel(val, xram);
79 xram += sizeof(u32);
80 size -= sizeof(u32);
81 }
82}
83
84#ifdef HAVE_SOCAL_UCODE
85static void socal_bzero(unsigned long xram, int size)
86{
87 while (size) {
88 sbus_writel(0, xram);
89 xram += sizeof(u32);
90 size -= sizeof(u32);
91 }
92}
93#endif
94
95static inline void socal_disable(struct socal *s)
96{
97 sbus_writel(0, s->regs + IMASK);
98 sbus_writel(SOCAL_CMD_SOFT_RESET, s->regs + CMD);
99}
100
101static inline void socal_enable(struct socal *s)
102{
103 SOD(("enable %08x\n", s->cfg))
104 sbus_writel(0, s->regs + SAE);
105 sbus_writel(s->cfg, s->regs + CFG);
106 sbus_writel(SOCAL_CMD_RSP_QALL, s->regs + CMD);
107 SOCAL_SETIMASK(s, SOCAL_IMASK_RSP_QALL | SOCAL_IMASK_SAE);
108 SOD(("imask %08x %08x\n", s->imask, sbus_readl(s->regs + IMASK)));
109}
110
111static void socal_reset(fc_channel *fc)
112{
113 socal_port *port = (socal_port *)fc;
114 struct socal *s = port->s;
115
116 /* FIXME */
117 socal_disable(s);
118 s->req[0].seqno = 1;
119 s->req[1].seqno = 1;
120 s->rsp[0].seqno = 1;
121 s->rsp[1].seqno = 1;
122 s->req[0].in = 0;
123 s->req[1].in = 0;
124 s->rsp[0].in = 0;
125 s->rsp[1].in = 0;
126 s->req[0].out = 0;
127 s->req[1].out = 0;
128 s->rsp[0].out = 0;
129 s->rsp[1].out = 0;
130
131 /* FIXME */
132 socal_enable(s);
133}
134
135static inline void socal_solicited(struct socal *s, unsigned long qno)
136{
137 socal_rsp *hwrsp;
138 socal_cq *sw_cq;
139 int token;
140 int status;
141 fc_channel *fc;
142
143 sw_cq = &s->rsp[qno];
144
145 /* Finally an improvement against old SOC :) */
146 sw_cq->in = sbus_readb(s->regs + RESP + qno);
147 SOD (("socal_solicited, %d packets arrived\n",
148 (sw_cq->in - sw_cq->out) & sw_cq->last))
149 for (;;) {
150 hwrsp = (socal_rsp *)sw_cq->pool + sw_cq->out;
151 SOD(("hwrsp %p out %d\n", hwrsp, sw_cq->out))
152
153#if defined(SOCALDEBUG) && 0
154 {
155 u32 *u = (u32 *)hwrsp;
156 SOD(("%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x\n",
157 u[0],u[1],u[2],u[3],u[4],u[5],u[6],u[7]))
158 u += 8;
159 SOD(("%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x\n",
160 u[0],u[1],u[2],u[3],u[4],u[5],u[6],u[7]))
161 u = (u32 *)s->xram;
162 while (u < ((u32 *)s->regs)) {
163 if (sbus_readl(&u[0]) == 0x00003000 ||
164 sbus_readl(&u[0]) == 0x00003801) {
165 SOD(("Found at %04lx\n",
166 (unsigned long)u - (unsigned long)s->xram))
167 SOD((" %08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x\n",
168 sbus_readl(&u[0]), sbus_readl(&u[1]),
169 sbus_readl(&u[2]), sbus_readl(&u[3]),
170 sbus_readl(&u[4]), sbus_readl(&u[5]),
171 sbus_readl(&u[6]), sbus_readl(&u[7])))
172 u += 8;
173 SOD((" %08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x\n",
174 sbus_readl(&u[0]), sbus_readl(&u[1]),
175 sbus_readl(&u[2]), sbus_readl(&u[3]),
176 sbus_readl(&u[4]), sbus_readl(&u[5]),
177 sbus_readl(&u[6]), sbus_readl(&u[7])))
178 u -= 8;
179 }
180 u++;
181 }
182 }
183#endif
184
185 token = hwrsp->shdr.token;
186 status = hwrsp->status;
187 fc = (fc_channel *)(&s->port[(token >> 11) & 1]);
188
189 SOD(("Solicited token %08x status %08x\n", token, status))
190 if (status == SOCAL_OK) {
191 fcp_receive_solicited(fc, token >> 12,
192 token & ((1 << 11) - 1),
193 FC_STATUS_OK, NULL);
194 } else {
195 /* We have intentionally defined FC_STATUS_* constants
196 * to match SOCAL_* constants, otherwise we'd have to
197 * translate status.
198 */
199 fcp_receive_solicited(fc, token >> 12,
200 token & ((1 << 11) - 1), status, &hwrsp->fchdr);
201 }
202
203 if (++sw_cq->out > sw_cq->last) {
204 sw_cq->seqno++;
205 sw_cq->out = 0;
206 }
207
208 if (sw_cq->out == sw_cq->in) {
209 sw_cq->in = sbus_readb(s->regs + RESP + qno);
210 if (sw_cq->out == sw_cq->in) {
211 /* Tell the hardware about it */
212 sbus_writel((sw_cq->out << 24) |
213 (SOCAL_CMD_RSP_QALL &
214 ~(SOCAL_CMD_RSP_Q0 << qno)),
215 s->regs + CMD);
216
217 /* Read it, so that we're sure it has been updated */
218 sbus_readl(s->regs + CMD);
219 sw_cq->in = sbus_readb(s->regs + RESP + qno);
220 if (sw_cq->out == sw_cq->in)
221 break;
222 }
223 }
224 }
225}
226
227static inline void socal_request (struct socal *s, u32 cmd)
228{
229 SOCAL_SETIMASK(s, s->imask & ~(cmd & SOCAL_CMD_REQ_QALL));
230 SOD(("imask %08x %08x\n", s->imask, sbus_readl(s->regs + IMASK)));
231
232 SOD(("Queues available %08x OUT %X\n", cmd, s->regs->reqpr[0]))
233 if (s->port[s->curr_port].fc.state != FC_STATE_OFFLINE) {
234 fcp_queue_empty ((fc_channel *)&(s->port[s->curr_port]));
235 if (((s->req[1].in + 1) & s->req[1].last) != (s->req[1].out))
236 fcp_queue_empty ((fc_channel *)&(s->port[1 - s->curr_port]));
237 } else {
238 fcp_queue_empty ((fc_channel *)&(s->port[1 - s->curr_port]));
239 }
240 if (s->port[1 - s->curr_port].fc.state != FC_STATE_OFFLINE)
241 s->curr_port ^= 1;
242}
243
244static inline void socal_unsolicited (struct socal *s, unsigned long qno)
245{
246 socal_rsp *hwrsp, *hwrspc;
247 socal_cq *sw_cq;
248 int count;
249 int status;
250 int flags;
251 fc_channel *fc;
252
253 sw_cq = &s->rsp[qno];
254
255 sw_cq->in = sbus_readb(s->regs + RESP + qno);
256 SOD (("socal_unsolicited, %d packets arrived, in %d\n",
257 (sw_cq->in - sw_cq->out) & sw_cq->last, sw_cq->in))
258 while (sw_cq->in != sw_cq->out) {
259 /* ...real work per entry here... */
260 hwrsp = (socal_rsp *)sw_cq->pool + sw_cq->out;
261 SOD(("hwrsp %p out %d\n", hwrsp, sw_cq->out))
262
263#if defined(SOCALDEBUG) && 0
264 {
265 u32 *u = (u32 *)hwrsp;
266 SOD(("%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x\n",
267 u[0],u[1],u[2],u[3],u[4],u[5],u[6],u[7]))
268 u += 8;
269 SOD(("%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x\n",
270 u[0],u[1],u[2],u[3],u[4],u[5],u[6],u[7]))
271 }
272#endif
273
274 hwrspc = NULL;
275 flags = hwrsp->shdr.flags;
276 count = hwrsp->count;
277 fc = (fc_channel *)&s->port[flags & SOCAL_PORT_B];
278 SOD(("FC %08lx\n", (long)fc))
279
280 if (count != 1) {
281 /* Ugh, continuation entries */
282 u8 in;
283
284 if (count != 2) {
285 printk("%s: Too many continuations entries %d\n",
286 fc->name, count);
287 goto update_out;
288 }
289
290 in = sw_cq->in;
291 if (in < sw_cq->out)
292 in += sw_cq->last + 1;
293 if (in < sw_cq->out + 2) {
294 /* Ask the hardware if they haven't arrived yet. */
295 sbus_writel((sw_cq->out << 24) |
296 (SOCAL_CMD_RSP_QALL &
297 ~(SOCAL_CMD_RSP_Q0 << qno)),
298 s->regs + CMD);
299
300 /* Read it, so that we're sure it has been updated */
301 sbus_readl(s->regs + CMD);
302 sw_cq->in = sbus_readb(s->regs + RESP + qno);
303 in = sw_cq->in;
304 if (in < sw_cq->out)
305 in += sw_cq->last + 1;
306 if (in < sw_cq->out + 2) /* Nothing came, let us wait */
307 return;
308 }
309 if (sw_cq->out == sw_cq->last)
310 hwrspc = (socal_rsp *)sw_cq->pool;
311 else
312 hwrspc = hwrsp + 1;
313 }
314
315 switch (flags & ~SOCAL_PORT_B) {
316 case SOCAL_STATUS:
317 status = hwrsp->status;
318 switch (status) {
319 case SOCAL_ONLINE:
320 SOD(("State change to ONLINE\n"));
321 fcp_state_change(fc, FC_STATE_ONLINE);
322 break;
323 case SOCAL_ONLINE_LOOP:
324 SOD(("State change to ONLINE_LOOP\n"));
325 fcp_state_change(fc, FC_STATE_ONLINE);
326 break;
327 case SOCAL_OFFLINE:
328 SOD(("State change to OFFLINE\n"));
329 fcp_state_change(fc, FC_STATE_OFFLINE);
330 break;
331 default:
332 printk ("%s: Unknown STATUS no %d\n",
333 fc->name, status);
334 break;
335 };
336
337 break;
338 case (SOCAL_UNSOLICITED|SOCAL_FC_HDR):
339 {
340 int r_ctl = *((u8 *)&hwrsp->fchdr);
341 unsigned len;
342
343 if ((r_ctl & 0xf0) == R_CTL_EXTENDED_SVC) {
344 len = hwrsp->shdr.bytecnt;
345 if (len < 4 || !hwrspc) {
346 printk ("%s: Invalid R_CTL %02x "
347 "continuation entries\n",
348 fc->name, r_ctl);
349 } else {
350 if (len > 60)
351 len = 60;
352 if (*(u32 *)hwrspc == LS_DISPLAY) {
353 int i;
354
355 for (i = 4; i < len; i++)
356 if (((u8 *)hwrspc)[i] == '\n')
357 ((u8 *)hwrspc)[i] = ' ';
358 ((u8 *)hwrspc)[len] = 0;
359 printk ("%s message: %s\n",
360 fc->name, ((u8 *)hwrspc) + 4);
361 } else {
362 printk ("%s: Unknown LS_CMD "
363 "%08x\n", fc->name,
364 *(u32 *)hwrspc);
365 }
366 }
367 } else {
368 printk ("%s: Unsolicited R_CTL %02x "
369 "not handled\n", fc->name, r_ctl);
370 }
371 }
372 break;
373 default:
374 printk ("%s: Unexpected flags %08x\n", fc->name, flags);
375 break;
376 };
377update_out:
378 if (++sw_cq->out > sw_cq->last) {
379 sw_cq->seqno++;
380 sw_cq->out = 0;
381 }
382
383 if (hwrspc) {
384 if (++sw_cq->out > sw_cq->last) {
385 sw_cq->seqno++;
386 sw_cq->out = 0;
387 }
388 }
389
390 if (sw_cq->out == sw_cq->in) {
391 sw_cq->in = sbus_readb(s->regs + RESP + qno);
392 if (sw_cq->out == sw_cq->in) {
393 /* Tell the hardware about it */
394 sbus_writel((sw_cq->out << 24) |
395 (SOCAL_CMD_RSP_QALL &
396 ~(SOCAL_CMD_RSP_Q0 << qno)),
397 s->regs + CMD);
398
399 /* Read it, so that we're sure it has been updated */
400 sbus_readl(s->regs + CMD);
401 sw_cq->in = sbus_readb(s->regs + RESP + qno);
402 }
403 }
404 }
405}
406
407static irqreturn_t socal_intr(int irq, void *dev_id, struct pt_regs *regs)
408{
409 u32 cmd;
410 unsigned long flags;
411 register struct socal *s = (struct socal *)dev_id;
412
413 spin_lock_irqsave(&s->lock, flags);
414 cmd = sbus_readl(s->regs + CMD);
415 for (; (cmd = SOCAL_INTR (s, cmd)); cmd = sbus_readl(s->regs + CMD)) {
416#ifdef SOCALDEBUG
417 static int cnt = 0;
418 if (cnt++ < 50)
419 printk("soc_intr %08x\n", cmd);
420#endif
421 if (cmd & SOCAL_CMD_RSP_Q2)
422 socal_unsolicited (s, SOCAL_UNSOLICITED_RSP_Q);
423 if (cmd & SOCAL_CMD_RSP_Q1)
424 socal_unsolicited (s, SOCAL_SOLICITED_BAD_RSP_Q);
425 if (cmd & SOCAL_CMD_RSP_Q0)
426 socal_solicited (s, SOCAL_SOLICITED_RSP_Q);
427 if (cmd & SOCAL_CMD_REQ_QALL)
428 socal_request (s, cmd);
429 }
430 spin_unlock_irqrestore(&s->lock, flags);
431
432 return IRQ_HANDLED;
433}
434
435#define TOKEN(proto, port, token) (((proto)<<12)|(token)|(port))
436
437static int socal_hw_enque (fc_channel *fc, fcp_cmnd *fcmd)
438{
439 socal_port *port = (socal_port *)fc;
440 struct socal *s = port->s;
441 unsigned long qno;
442 socal_cq *sw_cq;
443 int cq_next_in;
444 socal_req *request;
445 fc_hdr *fch;
446 int i;
447
448 if (fcmd->proto == TYPE_SCSI_FCP)
449 qno = 1;
450 else
451 qno = 0;
452 SOD(("Putting a FCP packet type %d into hw queue %d\n", fcmd->proto, qno))
453 if (s->imask & (SOCAL_IMASK_REQ_Q0 << qno)) {
454 SOD(("EIO %08x\n", s->imask))
455 return -EIO;
456 }
457 sw_cq = s->req + qno;
458 cq_next_in = (sw_cq->in + 1) & sw_cq->last;
459
460 if (cq_next_in == sw_cq->out &&
461 cq_next_in == (sw_cq->out = sbus_readb(s->regs + REQP + qno))) {
462 SOD(("%d IN %d OUT %d LAST %d\n",
463 qno, sw_cq->in,
464 sw_cq->out, sw_cq->last))
465 SOCAL_SETIMASK(s, s->imask | (SOCAL_IMASK_REQ_Q0 << qno));
466 SOD(("imask %08x %08x\n", s->imask, sbus_readl(s->regs + IMASK)));
467
468 /* If queue is full, just say NO. */
469 return -EBUSY;
470 }
471
472 request = sw_cq->pool + sw_cq->in;
473 fch = &request->fchdr;
474
475 switch (fcmd->proto) {
476 case TYPE_SCSI_FCP:
477 request->shdr.token = TOKEN(TYPE_SCSI_FCP, port->mask, fcmd->token);
478 request->data[0].base = fc->dma_scsi_cmd + fcmd->token * sizeof(fcp_cmd);
479 request->data[0].count = sizeof(fcp_cmd);
480 request->data[1].base = fc->dma_scsi_rsp + fcmd->token * fc->rsp_size;
481 request->data[1].count = fc->rsp_size;
482 if (fcmd->data) {
483 request->shdr.segcnt = 3;
484 i = fc->scsi_cmd_pool[fcmd->token].fcp_data_len;
485 request->shdr.bytecnt = i;
486 request->data[2].base = fcmd->data;
487 request->data[2].count = i;
488 request->type = (fc->scsi_cmd_pool[fcmd->token].fcp_cntl & FCP_CNTL_WRITE) ?
489 SOCAL_CQTYPE_IO_WRITE : SOCAL_CQTYPE_IO_READ;
490 } else {
491 request->shdr.segcnt = 2;
492 request->shdr.bytecnt = 0;
493 request->data[2].base = 0;
494 request->data[2].count = 0;
495 request->type = SOCAL_CQTYPE_SIMPLE;
496 }
497 FILL_FCHDR_RCTL_DID(fch, R_CTL_COMMAND, fcmd->did);
498 FILL_FCHDR_SID(fch, fc->sid);
499 FILL_FCHDR_TYPE_FCTL(fch, TYPE_SCSI_FCP, F_CTL_FIRST_SEQ | F_CTL_SEQ_INITIATIVE);
500 FILL_FCHDR_SEQ_DF_SEQ(fch, 0, 0, 0);
501 FILL_FCHDR_OXRX(fch, 0xffff, 0xffff);
502 fch->param = 0;
503 request->shdr.flags = port->flags;
504 request->shdr.class = fc->posmap ? 3 : 2;
505 break;
506
507 case PROTO_OFFLINE:
508 memset (request, 0, sizeof(*request));
509 request->shdr.token = TOKEN(PROTO_OFFLINE, port->mask, fcmd->token);
510 request->type = SOCAL_CQTYPE_OFFLINE;
511 FILL_FCHDR_RCTL_DID(fch, R_CTL_COMMAND, fcmd->did);
512 FILL_FCHDR_SID(fch, fc->sid);
513 FILL_FCHDR_TYPE_FCTL(fch, TYPE_SCSI_FCP, F_CTL_FIRST_SEQ | F_CTL_SEQ_INITIATIVE);
514 FILL_FCHDR_SEQ_DF_SEQ(fch, 0, 0, 0);
515 FILL_FCHDR_OXRX(fch, 0xffff, 0xffff);
516 request->shdr.flags = port->flags;
517 break;
518
519 case PROTO_REPORT_AL_MAP:
520 memset (request, 0, sizeof(*request));
521 request->shdr.token = TOKEN(PROTO_REPORT_AL_MAP, port->mask, fcmd->token);
522 request->type = SOCAL_CQTYPE_REPORT_MAP;
523 request->shdr.flags = port->flags;
524 request->shdr.segcnt = 1;
525 request->shdr.bytecnt = sizeof(fc_al_posmap);
526 request->data[0].base = fcmd->cmd;
527 request->data[0].count = sizeof(fc_al_posmap);
528 break;
529
530 default:
531 request->shdr.token = TOKEN(fcmd->proto, port->mask, fcmd->token);
532 request->shdr.class = fc->posmap ? 3 : 2;
533 request->shdr.flags = port->flags;
534 memcpy (fch, &fcmd->fch, sizeof(fc_hdr));
535 request->data[0].count = fcmd->cmdlen;
536 request->data[1].count = fcmd->rsplen;
537 request->type = fcmd->class;
538 switch (fcmd->class) {
539 case FC_CLASS_OUTBOUND:
540 request->data[0].base = fcmd->cmd;
541 request->data[0].count = fcmd->cmdlen;
542 request->type = SOCAL_CQTYPE_OUTBOUND;
543 request->shdr.bytecnt = fcmd->cmdlen;
544 request->shdr.segcnt = 1;
545 break;
546 case FC_CLASS_INBOUND:
547 request->data[0].base = fcmd->rsp;
548 request->data[0].count = fcmd->rsplen;
549 request->type = SOCAL_CQTYPE_INBOUND;
550 request->shdr.bytecnt = 0;
551 request->shdr.segcnt = 1;
552 break;
553 case FC_CLASS_SIMPLE:
554 request->data[0].base = fcmd->cmd;
555 request->data[1].base = fcmd->rsp;
556 request->data[0].count = fcmd->cmdlen;
557 request->data[1].count = fcmd->rsplen;
558 request->type = SOCAL_CQTYPE_SIMPLE;
559 request->shdr.bytecnt = fcmd->cmdlen;
560 request->shdr.segcnt = 2;
561 break;
562 case FC_CLASS_IO_READ:
563 case FC_CLASS_IO_WRITE:
564 request->data[0].base = fcmd->cmd;
565 request->data[1].base = fcmd->rsp;
566 request->data[0].count = fcmd->cmdlen;
567 request->data[1].count = fcmd->rsplen;
568 request->type = (fcmd->class == FC_CLASS_IO_READ) ? SOCAL_CQTYPE_IO_READ : SOCAL_CQTYPE_IO_WRITE;
569 if (fcmd->data) {
570 request->data[2].base = fcmd->data;
571 request->data[2].count = fcmd->datalen;
572 request->shdr.bytecnt = fcmd->datalen;
573 request->shdr.segcnt = 3;
574 } else {
575 request->shdr.bytecnt = 0;
576 request->shdr.segcnt = 2;
577 }
578 break;
579 }
580 break;
581 }
582
583 request->count = 1;
584 request->flags = 0;
585 request->seqno = sw_cq->seqno;
586
587 SOD(("queueing token %08x\n", request->shdr.token))
588
589 /* And now tell the SOCAL about it */
590
591 if (++sw_cq->in > sw_cq->last) {
592 sw_cq->in = 0;
593 sw_cq->seqno++;
594 }
595
596 SOD(("Putting %08x into cmd\n", SOCAL_CMD_RSP_QALL | (sw_cq->in << 24) | (SOCAL_CMD_REQ_Q0 << qno)))
597
598 sbus_writel(SOCAL_CMD_RSP_QALL | (sw_cq->in << 24) | (SOCAL_CMD_REQ_Q0 << qno),
599 s->regs + CMD);
600
601 /* Read so that command is completed */
602 sbus_readl(s->regs + CMD);
603
604 return 0;
605}
606
607static inline void socal_download_fw(struct socal *s)
608{
609#ifdef HAVE_SOCAL_UCODE
610 SOD(("Loading %ld bytes from %p to %p\n", sizeof(socal_ucode), socal_ucode, s->xram))
611 socal_copy_to_xram(s->xram, socal_ucode, sizeof(socal_ucode));
612 SOD(("Clearing the rest of memory\n"))
613 socal_bzero (s->xram + sizeof(socal_ucode), 65536 - sizeof(socal_ucode));
614 SOD(("Done\n"))
615#endif
616}
617
618/* Check for what the best SBUS burst we can use happens
619 * to be on this machine.
620 */
621static inline void socal_init_bursts(struct socal *s, struct sbus_dev *sdev)
622{
623 int bsizes, bsizes_more;
624 u32 cfg;
625
626 bsizes = (prom_getintdefault(sdev->prom_node,"burst-sizes",0xff) & 0xff);
627 bsizes_more = (prom_getintdefault(sdev->bus->prom_node, "burst-sizes", 0xff) & 0xff);
628 bsizes &= bsizes_more;
629#ifdef USE_64BIT_MODE
630#ifdef __sparc_v9__
631 mmu_set_sbus64(sdev, bsizes >> 16);
632#endif
633#endif
634 if ((bsizes & 0x7f) == 0x7f)
635 cfg = SOCAL_CFG_BURST_64;
636 else if ((bsizes & 0x3f) == 0x3f)
637 cfg = SOCAL_CFG_BURST_32;
638 else if ((bsizes & 0x1f) == 0x1f)
639 cfg = SOCAL_CFG_BURST_16;
640 else
641 cfg = SOCAL_CFG_BURST_4;
642#ifdef USE_64BIT_MODE
643#ifdef __sparc_v9__
644 /* What is BURST_128? -jj */
645 if ((bsizes & 0x780000) == 0x780000)
646 cfg |= (SOCAL_CFG_BURST_64 << 8) | SOCAL_CFG_SBUS_ENHANCED;
647 else if ((bsizes & 0x380000) == 0x380000)
648 cfg |= (SOCAL_CFG_BURST_32 << 8) | SOCAL_CFG_SBUS_ENHANCED;
649 else if ((bsizes & 0x180000) == 0x180000)
650 cfg |= (SOCAL_CFG_BURST_16 << 8) | SOCAL_CFG_SBUS_ENHANCED;
651 else
652 cfg |= (SOCAL_CFG_BURST_8 << 8) | SOCAL_CFG_SBUS_ENHANCED;
653#endif
654#endif
655 s->cfg = cfg;
656}
657
658static inline void socal_init(struct sbus_dev *sdev, int no)
659{
660 unsigned char tmp[60];
661 int propl;
662 struct socal *s;
663 static unsigned version_printed = 0;
664 socal_hw_cq cq[8];
665 int size, i;
666 int irq, node;
667
668 s = kmalloc (sizeof (struct socal), GFP_KERNEL);
669 if (!s) return;
670 memset (s, 0, sizeof(struct socal));
671 spin_lock_init(&s->lock);
672 s->socal_no = no;
673
674 SOD(("socals %08lx socal_intr %08lx socal_hw_enque %08lx\n",
675 (long)socals, (long)socal_intr, (long)socal_hw_enque))
676 if (version_printed++ == 0)
677 printk (version);
678
679 s->port[0].fc.module = THIS_MODULE;
680 s->port[1].fc.module = THIS_MODULE;
681
682 s->next = socals;
683 socals = s;
684 s->port[0].fc.dev = sdev;
685 s->port[1].fc.dev = sdev;
686 s->port[0].s = s;
687 s->port[1].s = s;
688
689 s->port[0].fc.next = &s->port[1].fc;
690
691 /* World Wide Name of SOCAL */
692 propl = prom_getproperty (sdev->prom_node, "wwn", tmp, sizeof(tmp));
693 if (propl != sizeof (fc_wwn)) {
694 s->wwn.naaid = NAAID_IEEE_REG;
695 s->wwn.nportid = 0x123;
696 s->wwn.hi = 0x1234;
697 s->wwn.lo = 0x12345678;
698 } else
699 memcpy (&s->wwn, tmp, sizeof (fc_wwn));
700
701 memcpy (&s->port[0].fc.wwn_nport, &s->wwn, sizeof (fc_wwn));
702 s->port[0].fc.wwn_nport.lo++;
703 memcpy (&s->port[1].fc.wwn_nport, &s->wwn, sizeof (fc_wwn));
704 s->port[1].fc.wwn_nport.lo+=2;
705
706 node = prom_getchild (sdev->prom_node);
707 while (node && (node = prom_searchsiblings (node, "sf"))) {
708 int port;
709
710 port = prom_getintdefault(node, "port#", -1);
711 switch (port) {
712 case 0:
713 case 1:
714 if (prom_getproplen(node, "port-wwn") == sizeof (fc_wwn))
715 prom_getproperty (node, "port-wwn",
716 (char *)&s->port[port].fc.wwn_nport,
717 sizeof (fc_wwn));
718 break;
719 default:
720 break;
721 };
722
723 node = prom_getsibling(node);
724 }
725
726 memcpy (&s->port[0].fc.wwn_node, &s->wwn, sizeof (fc_wwn));
727 memcpy (&s->port[1].fc.wwn_node, &s->wwn, sizeof (fc_wwn));
728 SOD(("Got wwns %08x%08x ports %08x%08x and %08x%08x\n",
729 *(u32 *)&s->port[0].fc.wwn_node, s->port[0].fc.wwn_node.lo,
730 *(u32 *)&s->port[0].fc.wwn_nport, s->port[0].fc.wwn_nport.lo,
731 *(u32 *)&s->port[1].fc.wwn_nport, s->port[1].fc.wwn_nport.lo))
732
733 s->port[0].fc.sid = 1;
734 s->port[1].fc.sid = 17;
735 s->port[0].fc.did = 2;
736 s->port[1].fc.did = 18;
737
738 s->port[0].fc.reset = socal_reset;
739 s->port[1].fc.reset = socal_reset;
740
741 if (sdev->num_registers == 1) {
742 s->eeprom = sbus_ioremap(&sdev->resource[0], 0,
743 sdev->reg_addrs[0].reg_size, "socal xram");
744 if (sdev->reg_addrs[0].reg_size > 0x20000)
745 s->xram = s->eeprom + 0x10000UL;
746 else
747 s->xram = s->eeprom;
748 s->regs = (s->xram + 0x10000UL);
749 } else {
750 /* E.g. starfire presents 3 registers for SOCAL */
751 s->xram = sbus_ioremap(&sdev->resource[1], 0,
752 sdev->reg_addrs[1].reg_size, "socal xram");
753 s->regs = sbus_ioremap(&sdev->resource[2], 0,
754 sdev->reg_addrs[2].reg_size, "socal regs");
755 }
756
757 socal_init_bursts(s, sdev);
758
759 SOD(("Disabling SOCAL\n"))
760
761 socal_disable (s);
762
763 irq = sdev->irqs[0];
764
765 if (request_irq (irq, socal_intr, SA_SHIRQ, "SOCAL", (void *)s)) {
766 socal_printk ("Cannot order irq %d to go\n", irq);
767 socals = s->next;
768 return;
769 }
770
771 SOD(("SOCAL uses IRQ %s\n", __irq_itoa(irq)))
772
773 s->port[0].fc.irq = irq;
774 s->port[1].fc.irq = irq;
775
776 sprintf (s->port[0].fc.name, "socal%d port A", no);
777 sprintf (s->port[1].fc.name, "socal%d port B", no);
778 s->port[0].flags = SOCAL_FC_HDR | SOCAL_PORT_A;
779 s->port[1].flags = SOCAL_FC_HDR | SOCAL_PORT_B;
780 s->port[1].mask = (1 << 11);
781
782 s->port[0].fc.hw_enque = socal_hw_enque;
783 s->port[1].fc.hw_enque = socal_hw_enque;
784
785 socal_download_fw (s);
786
787 SOD(("Downloaded firmware\n"))
788
789 /* Now setup xram circular queues */
790 memset (cq, 0, sizeof(cq));
791
792 size = (SOCAL_CQ_REQ0_SIZE + SOCAL_CQ_REQ1_SIZE +
793 SOCAL_CQ_RSP0_SIZE + SOCAL_CQ_RSP1_SIZE +
794 SOCAL_CQ_RSP2_SIZE) * sizeof(socal_req);
795 s->req_cpu = sbus_alloc_consistent(sdev, size, &s->req_dvma);
796 s->req[0].pool = s->req_cpu;
797 cq[0].address = s->req_dvma;
798 s->req[1].pool = s->req[0].pool + SOCAL_CQ_REQ0_SIZE;
799 s->rsp[0].pool = s->req[1].pool + SOCAL_CQ_REQ1_SIZE;
800 s->rsp[1].pool = s->rsp[0].pool + SOCAL_CQ_RSP0_SIZE;
801 s->rsp[2].pool = s->rsp[1].pool + SOCAL_CQ_RSP1_SIZE;
802
803 s->req[0].hw_cq = (socal_hw_cq __iomem *)(s->xram + SOCAL_CQ_REQ_OFFSET);
804 s->req[1].hw_cq = (socal_hw_cq __iomem *)(s->xram + SOCAL_CQ_REQ_OFFSET + sizeof(socal_hw_cq));
805 s->rsp[0].hw_cq = (socal_hw_cq __iomem *)(s->xram + SOCAL_CQ_RSP_OFFSET);
806 s->rsp[1].hw_cq = (socal_hw_cq __iomem *)(s->xram + SOCAL_CQ_RSP_OFFSET + sizeof(socal_hw_cq));
807 s->rsp[2].hw_cq = (socal_hw_cq __iomem *)(s->xram + SOCAL_CQ_RSP_OFFSET + 2 * sizeof(socal_hw_cq));
808
809 cq[1].address = cq[0].address + (SOCAL_CQ_REQ0_SIZE * sizeof(socal_req));
810 cq[4].address = cq[1].address + (SOCAL_CQ_REQ1_SIZE * sizeof(socal_req));
811 cq[5].address = cq[4].address + (SOCAL_CQ_RSP0_SIZE * sizeof(socal_req));
812 cq[6].address = cq[5].address + (SOCAL_CQ_RSP1_SIZE * sizeof(socal_req));
813
814 cq[0].last = SOCAL_CQ_REQ0_SIZE - 1;
815 cq[1].last = SOCAL_CQ_REQ1_SIZE - 1;
816 cq[4].last = SOCAL_CQ_RSP0_SIZE - 1;
817 cq[5].last = SOCAL_CQ_RSP1_SIZE - 1;
818 cq[6].last = SOCAL_CQ_RSP2_SIZE - 1;
819 for (i = 0; i < 8; i++)
820 cq[i].seqno = 1;
821
822 s->req[0].last = SOCAL_CQ_REQ0_SIZE - 1;
823 s->req[1].last = SOCAL_CQ_REQ1_SIZE - 1;
824 s->rsp[0].last = SOCAL_CQ_RSP0_SIZE - 1;
825 s->rsp[1].last = SOCAL_CQ_RSP1_SIZE - 1;
826 s->rsp[2].last = SOCAL_CQ_RSP2_SIZE - 1;
827
828 s->req[0].seqno = 1;
829 s->req[1].seqno = 1;
830 s->rsp[0].seqno = 1;
831 s->rsp[1].seqno = 1;
832 s->rsp[2].seqno = 1;
833
834 socal_copy_to_xram(s->xram + SOCAL_CQ_REQ_OFFSET, cq, sizeof(cq));
835
836 SOD(("Setting up params\n"))
837
838 /* Make our sw copy of SOCAL service parameters */
839 socal_copy_from_xram(s->serv_params, s->xram + 0x280, sizeof (s->serv_params));
840
841 s->port[0].fc.common_svc = (common_svc_parm *)s->serv_params;
842 s->port[0].fc.class_svcs = (svc_parm *)(s->serv_params + 0x20);
843 s->port[1].fc.common_svc = (common_svc_parm *)&s->serv_params;
844 s->port[1].fc.class_svcs = (svc_parm *)(s->serv_params + 0x20);
845
846 socal_enable (s);
847
848 SOD(("Enabled SOCAL\n"))
849}
850
851static int __init socal_probe(void)
852{
853 struct sbus_bus *sbus;
854 struct sbus_dev *sdev = NULL;
855 struct socal *s;
856 int cards = 0;
857
858 for_each_sbus(sbus) {
859 for_each_sbusdev(sdev, sbus) {
860 if(!strcmp(sdev->prom_name, "SUNW,socal")) {
861 socal_init(sdev, cards);
862 cards++;
863 }
864 }
865 }
866 if (!cards)
867 return -EIO;
868
869 for_each_socal(s)
870 if (s->next)
871 s->port[1].fc.next = &s->next->port[0].fc;
872
873 fcp_init (&socals->port[0].fc);
874 return 0;
875}
876
877static void __exit socal_cleanup(void)
878{
879 struct socal *s;
880 int irq;
881 struct sbus_dev *sdev;
882
883 for_each_socal(s) {
884 irq = s->port[0].fc.irq;
885 free_irq (irq, s);
886
887 fcp_release(&(s->port[0].fc), 2);
888
889 sdev = s->port[0].fc.dev;
890 if (sdev->num_registers == 1) {
891 sbus_iounmap(s->eeprom, sdev->reg_addrs[0].reg_size);
892 } else {
893 sbus_iounmap(s->xram, sdev->reg_addrs[1].reg_size);
894 sbus_iounmap(s->regs, sdev->reg_addrs[2].reg_size);
895 }
896 sbus_free_consistent(sdev,
897 (SOCAL_CQ_REQ0_SIZE + SOCAL_CQ_REQ1_SIZE +
898 SOCAL_CQ_RSP0_SIZE + SOCAL_CQ_RSP1_SIZE +
899 SOCAL_CQ_RSP2_SIZE) * sizeof(socal_req),
900 s->req_cpu, s->req_dvma);
901 }
902}
903
904module_init(socal_probe);
905module_exit(socal_cleanup);
906MODULE_LICENSE("GPL");
diff --git a/drivers/fc4/socal.h b/drivers/fc4/socal.h
new file mode 100644
index 000000000000..774edf68e4d2
--- /dev/null
+++ b/drivers/fc4/socal.h
@@ -0,0 +1,314 @@
1/* socal.h: Definitions for Sparc SUNW,socal (SOC+) Fibre Channel Sbus driver.
2 *
3 * Copyright (C) 1998,1999 Jakub Jelinek (jj@ultra.linux.cz)
4 */
5
6#ifndef __SOCAL_H
7#define __SOCAL_H
8
9#include "fc.h"
10#include "fcp.h"
11#include "fcp_impl.h"
12
13/* Hardware register offsets and constants first {{{ */
14#define CFG 0x00UL
15#define SAE 0x04UL
16#define CMD 0x08UL
17#define IMASK 0x0cUL
18#define REQP 0x10UL
19#define RESP 0x14UL
20
21/* Config Register */
22#define SOCAL_CFG_EXT_RAM_BANK_MASK 0x07000000
23#define SOCAL_CFG_EEPROM_BANK_MASK 0x00030000
24#define SOCAL_CFG_BURST64_MASK 0x00000700
25#define SOCAL_CFG_SBUS_PARITY_TEST 0x00000020
26#define SOCAL_CFG_SBUS_PARITY_CHECK 0x00000010
27#define SOCAL_CFG_SBUS_ENHANCED 0x00000008
28#define SOCAL_CFG_BURST_MASK 0x00000007
29/* Bursts */
30#define SOCAL_CFG_BURST_4 0x00000000
31#define SOCAL_CFG_BURST_8 0x00000003
32#define SOCAL_CFG_BURST_16 0x00000004
33#define SOCAL_CFG_BURST_32 0x00000005
34#define SOCAL_CFG_BURST_64 0x00000006
35#define SOCAL_CFG_BURST_128 0x00000007
36
37/* Slave Access Error Register */
38#define SOCAL_SAE_ALIGNMENT 0x00000004
39#define SOCAL_SAE_UNSUPPORTED 0x00000002
40#define SOCAL_SAE_PARITY 0x00000001
41
42/* Command & Status Register */
43#define SOCAL_CMD_RSP_QALL 0x000f0000
44#define SOCAL_CMD_RSP_Q0 0x00010000
45#define SOCAL_CMD_RSP_Q1 0x00020000
46#define SOCAL_CMD_RSP_Q2 0x00040000
47#define SOCAL_CMD_RSP_Q3 0x00080000
48#define SOCAL_CMD_REQ_QALL 0x00000f00
49#define SOCAL_CMD_REQ_Q0 0x00000100
50#define SOCAL_CMD_REQ_Q1 0x00000200
51#define SOCAL_CMD_REQ_Q2 0x00000400
52#define SOCAL_CMD_REQ_Q3 0x00000800
53#define SOCAL_CMD_SAE 0x00000080
54#define SOCAL_CMD_INTR_PENDING 0x00000008
55#define SOCAL_CMD_NON_QUEUED 0x00000004
56#define SOCAL_CMD_IDLE 0x00000002
57#define SOCAL_CMD_SOFT_RESET 0x00000001
58
59/* Interrupt Mask Register */
60#define SOCAL_IMASK_RSP_QALL 0x000f0000
61#define SOCAL_IMASK_RSP_Q0 0x00010000
62#define SOCAL_IMASK_RSP_Q1 0x00020000
63#define SOCAL_IMASK_RSP_Q2 0x00040000
64#define SOCAL_IMASK_RSP_Q3 0x00080000
65#define SOCAL_IMASK_REQ_QALL 0x00000f00
66#define SOCAL_IMASK_REQ_Q0 0x00000100
67#define SOCAL_IMASK_REQ_Q1 0x00000200
68#define SOCAL_IMASK_REQ_Q2 0x00000400
69#define SOCAL_IMASK_REQ_Q3 0x00000800
70#define SOCAL_IMASK_SAE 0x00000080
71#define SOCAL_IMASK_NON_QUEUED 0x00000004
72
73#define SOCAL_INTR(s, cmd) \
74 (((cmd & SOCAL_CMD_RSP_QALL) | ((~cmd) & SOCAL_CMD_REQ_QALL)) \
75 & s->imask)
76
77#define SOCAL_SETIMASK(s, i) \
78do { (s)->imask = (i); \
79 sbus_writel((i), (s)->regs + IMASK); \
80} while (0)
81
82#define SOCAL_MAX_EXCHANGES 1024
83
84/* XRAM
85 *
86 * This is a 64KB register area.
87 * From the documentation, it seems like it is finally able to cope
88 * at least with 1,2,4 byte accesses for read and 2,4 byte accesses for write.
89 */
90
91/* Circular Queue */
92
93#define SOCAL_CQ_REQ_OFFSET 0x200
94#define SOCAL_CQ_RSP_OFFSET 0x220
95
96typedef struct {
97 u32 address;
98 u8 in;
99 u8 out;
100 u8 last;
101 u8 seqno;
102} socal_hw_cq;
103
104#define SOCAL_PORT_A 0x0000 /* From/To Port A */
105#define SOCAL_PORT_B 0x0001 /* From/To Port A */
106#define SOCAL_FC_HDR 0x0002 /* Contains FC Header */
107#define SOCAL_NORSP 0x0004 /* Don't generate response nor interrupt */
108#define SOCAL_NOINT 0x0008 /* Generate response but not interrupt */
109#define SOCAL_XFERRDY 0x0010 /* Generate XFERRDY */
110#define SOCAL_IGNOREPARAM 0x0020 /* Ignore PARAM field in the FC header */
111#define SOCAL_COMPLETE 0x0040 /* Command completed */
112#define SOCAL_UNSOLICITED 0x0080 /* For request this is the packet to establish unsolicited pools, */
113 /* for rsp this is unsolicited packet */
114#define SOCAL_STATUS 0x0100 /* State change (on/off line) */
115#define SOCAL_RSP_HDR 0x0200 /* Return frame header in any case */
116
117typedef struct {
118 u32 token;
119 u16 flags;
120 u8 class;
121 u8 segcnt;
122 u32 bytecnt;
123} socal_hdr;
124
125typedef struct {
126 u32 base;
127 u32 count;
128} socal_data;
129
130#define SOCAL_CQTYPE_NOP 0x00
131#define SOCAL_CQTYPE_OUTBOUND 0x01
132#define SOCAL_CQTYPE_INBOUND 0x02
133#define SOCAL_CQTYPE_SIMPLE 0x03
134#define SOCAL_CQTYPE_IO_WRITE 0x04
135#define SOCAL_CQTYPE_IO_READ 0x05
136#define SOCAL_CQTYPE_UNSOLICITED 0x06
137#define SOCAL_CQTYPE_BYPASS_DEV 0x06
138#define SOCAL_CQTYPE_DIAG 0x07
139#define SOCAL_CQTYPE_OFFLINE 0x08
140#define SOCAL_CQTYPE_ADD_POOL 0x09
141#define SOCAL_CQTYPE_DELETE_POOL 0x0a
142#define SOCAL_CQTYPE_ADD_BUFFER 0x0b
143#define SOCAL_CQTYPE_ADD_POOL_BUFFER 0x0c
144#define SOCAL_CQTYPE_REQUEST_ABORT 0x0d
145#define SOCAL_CQTYPE_REQUEST_LIP 0x0e
146#define SOCAL_CQTYPE_REPORT_MAP 0x0f
147#define SOCAL_CQTYPE_RESPONSE 0x10
148#define SOCAL_CQTYPE_INLINE 0x20
149
150#define SOCAL_CQFLAGS_CONT 0x01
151#define SOCAL_CQFLAGS_FULL 0x02
152#define SOCAL_CQFLAGS_BADHDR 0x04
153#define SOCAL_CQFLAGS_BADPKT 0x08
154
155typedef struct {
156 socal_hdr shdr;
157 socal_data data[3];
158 fc_hdr fchdr;
159 u8 count;
160 u8 type;
161 u8 flags;
162 u8 seqno;
163} socal_req;
164
165#define SOCAL_OK 0
166#define SOCAL_P_RJT 2
167#define SOCAL_F_RJT 3
168#define SOCAL_P_BSY 4
169#define SOCAL_F_BSY 5
170#define SOCAL_ONLINE 0x10
171#define SOCAL_OFFLINE 0x11
172#define SOCAL_TIMEOUT 0x12
173#define SOCAL_OVERRUN 0x13
174#define SOCAL_ONLINE_LOOP 0x14
175#define SOCAL_OLD_PORT 0x15
176#define SOCAL_AL_PORT 0x16
177#define SOCAL_UNKOWN_CQ_TYPE 0x20
178#define SOCAL_BAD_SEG_CNT 0x21
179#define SOCAL_MAX_XCHG_EXCEEDED 0x22
180#define SOCAL_BAD_XID 0x23
181#define SOCAL_XCHG_BUSY 0x24
182#define SOCAL_BAD_POOL_ID 0x25
183#define SOCAL_INSUFFICIENT_CQES 0x26
184#define SOCAL_ALLOC_FAIL 0x27
185#define SOCAL_BAD_SID 0x28
186#define SOCAL_NO_SEG_INIT 0x29
187#define SOCAL_BAD_DID 0x2a
188#define SOCAL_ABORTED 0x30
189#define SOCAL_ABORT_FAILED 0x31
190
191typedef struct {
192 socal_hdr shdr;
193 u32 status;
194 socal_data data;
195 u8 xxx1[10];
196 u16 ncmds;
197 fc_hdr fchdr;
198 u8 count;
199 u8 type;
200 u8 flags;
201 u8 seqno;
202} socal_rsp;
203
204typedef struct {
205 socal_hdr shdr;
206 u8 xxx1[48];
207 u8 count;
208 u8 type;
209 u8 flags;
210 u8 seqno;
211} socal_cmdonly;
212
213#define SOCAL_DIAG_NOP 0x00
214#define SOCAL_DIAG_INT_LOOP 0x01
215#define SOCAL_DIAG_EXT_LOOP 0x02
216#define SOCAL_DIAG_REM_LOOP 0x03
217#define SOCAL_DIAG_XRAM_TEST 0x04
218#define SOCAL_DIAG_SOC_TEST 0x05
219#define SOCAL_DIAG_HCB_TEST 0x06
220#define SOCAL_DIAG_SOCLB_TEST 0x07
221#define SOCAL_DIAG_SRDSLB_TEST 0x08
222#define SOCAL_DIAG_EXTOE_TEST 0x09
223
224typedef struct {
225 socal_hdr shdr;
226 u32 cmd;
227 u8 xxx1[44];
228 u8 count;
229 u8 type;
230 u8 flags;
231 u8 seqno;
232} socal_diag_req;
233
234#define SOCAL_POOL_MASK_RCTL 0x800000
235#define SOCAL_POOL_MASK_DID 0x700000
236#define SOCAL_POOL_MASK_SID 0x070000
237#define SOCAL_POOL_MASK_TYPE 0x008000
238#define SOCAL_POOL_MASK_F_CTL 0x007000
239#define SOCAL_POOL_MASK_SEQ_ID 0x000800
240#define SOCAL_POOL_MASK_D_CTL 0x000400
241#define SOCAL_POOL_MASK_SEQ_CNT 0x000300
242#define SOCAL_POOL_MASK_OX_ID 0x0000f0
243#define SOCAL_POOL_MASK_PARAM 0x00000f
244
245typedef struct {
246 socal_hdr shdr;
247 u32 pool_id;
248 u32 header_mask;
249 u32 buf_size;
250 u32 entries;
251 u8 xxx1[8];
252 fc_hdr fchdr;
253 u8 count;
254 u8 type;
255 u8 flags;
256 u8 seqno;
257} socal_pool_req;
258
259/* }}} */
260
261/* Now our software structures and constants we use to drive the beast {{{ */
262
263#define SOCAL_CQ_REQ0_SIZE 4
264#define SOCAL_CQ_REQ1_SIZE 256
265#define SOCAL_CQ_RSP0_SIZE 8
266#define SOCAL_CQ_RSP1_SIZE 4
267#define SOCAL_CQ_RSP2_SIZE 4
268
269#define SOCAL_SOLICITED_RSP_Q 0
270#define SOCAL_SOLICITED_BAD_RSP_Q 1
271#define SOCAL_UNSOLICITED_RSP_Q 2
272
273struct socal;
274
275typedef struct {
276 /* This must come first */
277 fc_channel fc;
278 struct socal *s;
279 u16 flags;
280 u16 mask;
281} socal_port;
282
283typedef struct {
284 socal_hw_cq __iomem *hw_cq; /* Related XRAM cq */
285 socal_req *pool;
286 u8 in;
287 u8 out;
288 u8 last;
289 u8 seqno;
290} socal_cq;
291
292struct socal {
293 spinlock_t lock;
294 socal_port port[2]; /* Every SOCAL has one or two FC ports */
295 socal_cq req[4]; /* Request CQs */
296 socal_cq rsp[4]; /* Response CQs */
297 int socal_no;
298 void __iomem *regs;
299 void __iomem *xram;
300 void __iomem *eeprom;
301 fc_wwn wwn;
302 u32 imask; /* Our copy of regs->imask */
303 u32 cfg; /* Our copy of regs->cfg */
304 char serv_params[80];
305 struct socal *next;
306 int curr_port; /* Which port will have priority to fcp_queue_empty */
307
308 socal_req * req_cpu;
309 u32 req_dvma;
310};
311
312/* }}} */
313
314#endif /* !(__SOCAL_H) */