diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /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/Kconfig | 81 | ||||
-rw-r--r-- | drivers/fc4/Makefile | 9 | ||||
-rw-r--r-- | drivers/fc4/fc-al.h | 27 | ||||
-rw-r--r-- | drivers/fc4/fc.c | 1158 | ||||
-rw-r--r-- | drivers/fc4/fc.h | 230 | ||||
-rw-r--r-- | drivers/fc4/fc_syms.c | 33 | ||||
-rw-r--r-- | drivers/fc4/fcp.h | 94 | ||||
-rw-r--r-- | drivers/fc4/fcp_impl.h | 164 | ||||
-rw-r--r-- | drivers/fc4/soc.c | 766 | ||||
-rw-r--r-- | drivers/fc4/soc.h | 301 | ||||
-rw-r--r-- | drivers/fc4/socal.c | 906 | ||||
-rw-r--r-- | drivers/fc4/socal.h | 314 |
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 | |||
5 | menu "Fibre Channel support" | ||
6 | |||
7 | config 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 | |||
24 | comment "FC4 drivers" | ||
25 | depends on FC4 | ||
26 | |||
27 | config 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 | |||
39 | config 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 | |||
52 | comment "FC4 targets" | ||
53 | depends on FC4 | ||
54 | |||
55 | config 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 | |||
64 | config 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 | |||
76 | config SCSI_FCAL | ||
77 | prompt "Generic FC-AL disk driver" | ||
78 | depends on FC4!=n && SCSI && !SPARC32 && !SPARC64 | ||
79 | |||
80 | endmenu | ||
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 | |||
5 | fc4-objs := fc.o fc_syms.o | ||
6 | |||
7 | obj-$(CONFIG_FC4) += fc4.o | ||
8 | obj-$(CONFIG_FC4_SOC) += soc.o | ||
9 | obj-$(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 | |||
21 | typedef 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 | |||
75 | static int fcp_scsi_queue_it(fc_channel *, Scsi_Cmnd *, fcp_cmnd *, int); | ||
76 | void fcp_queue_empty(fc_channel *); | ||
77 | |||
78 | static 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 | |||
92 | static 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 | |||
104 | fc_channel *fc_channels = NULL; | ||
105 | |||
106 | #define LSMAGIC 620829043 | ||
107 | typedef 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 | ||
120 | typedef 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 | ||
131 | typedef struct { | ||
132 | /* Must be first */ | ||
133 | struct semaphore sem; | ||
134 | int magic; | ||
135 | int status; | ||
136 | struct timer_list timer; | ||
137 | } lse; | ||
138 | |||
139 | static void fcp_login_timeout(unsigned long data) | ||
140 | { | ||
141 | ls *l = (ls *)data; | ||
142 | FCND(("Login timeout\n")) | ||
143 | up(&l->sem); | ||
144 | } | ||
145 | |||
146 | static 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 | |||
243 | static 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 | |||
337 | void 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 | |||
382 | static void fcp_scsi_done(Scsi_Cmnd *SCpnt); | ||
383 | |||
384 | static 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 | |||
464 | void 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 | |||
521 | void 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 | |||
532 | int 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 | } | ||
646 | all_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 | |||
663 | int 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 | |||
714 | int 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 | |||
738 | void 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 | |||
766 | static 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 | |||
776 | static 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 | |||
850 | int 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 | |||
873 | void 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 | |||
886 | int 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 | |||
926 | void 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 | |||
936 | int 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 | |||
1009 | int fcp_scsi_bus_reset(Scsi_Cmnd *SCpnt) | ||
1010 | { | ||
1011 | printk ("FC: bus reset!\n"); | ||
1012 | return FAILED; | ||
1013 | } | ||
1014 | |||
1015 | int 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 | |||
1036 | static 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 | |||
1048 | static 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 | |||
1104 | int 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 | |||
1134 | typedef struct { | ||
1135 | unsigned int code; | ||
1136 | unsigned params[4]; | ||
1137 | } prli; | ||
1138 | |||
1139 | int 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 | |||
1157 | MODULE_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 */ | ||
24 | typedef 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 | |||
97 | typedef 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 */ | ||
125 | typedef 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 | |||
195 | typedef 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 | |||
207 | typedef 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 */ | ||
220 | typedef 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 | |||
17 | EXPORT_SYMBOL(fcp_init); | ||
18 | EXPORT_SYMBOL(fcp_release); | ||
19 | EXPORT_SYMBOL(fcp_queue_empty); | ||
20 | EXPORT_SYMBOL(fcp_receive_solicited); | ||
21 | EXPORT_SYMBOL(fc_channels); | ||
22 | EXPORT_SYMBOL(fcp_state_change); | ||
23 | EXPORT_SYMBOL(fc_do_plogi); | ||
24 | EXPORT_SYMBOL(fc_do_prli); | ||
25 | |||
26 | /* SCSI stuff */ | ||
27 | EXPORT_SYMBOL(fcp_scsi_queuecommand); | ||
28 | EXPORT_SYMBOL(fcp_scsi_abort); | ||
29 | EXPORT_SYMBOL(fcp_scsi_dev_reset); | ||
30 | EXPORT_SYMBOL(fcp_scsi_bus_reset); | ||
31 | EXPORT_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 | |||
28 | typedef 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 | |||
41 | typedef 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 | |||
86 | typedef 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 | |||
37 | struct _fc_channel; | ||
38 | |||
39 | typedef 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 | |||
61 | typedef struct { | ||
62 | unsigned int len; | ||
63 | unsigned char list[0]; | ||
64 | } fcp_posmap; | ||
65 | |||
66 | typedef 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 | |||
111 | extern 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 | |||
143 | void fcp_queue_empty(fc_channel *); | ||
144 | int fcp_init(fc_channel *); | ||
145 | void fcp_release(fc_channel *fc_chain, int count); | ||
146 | void fcp_receive_solicited(fc_channel *, int, int, int, fc_hdr *); | ||
147 | void fcp_state_change(fc_channel *, int); | ||
148 | int fc_do_plogi(fc_channel *, unsigned char, fc_wwn *, fc_wwn *); | ||
149 | int 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 | |||
158 | int fcp_scsi_queuecommand(Scsi_Cmnd *, void (* done)(Scsi_Cmnd *)); | ||
159 | int fcp_scsi_abort(Scsi_Cmnd *); | ||
160 | int fcp_scsi_dev_reset(Scsi_Cmnd *); | ||
161 | int fcp_scsi_bus_reset(Scsi_Cmnd *); | ||
162 | int 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 | |||
20 | static 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) | ||
64 | struct soc *socs = NULL; | ||
65 | |||
66 | static 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 | |||
72 | static 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 | |||
82 | static 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 | |||
106 | static 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 | |||
167 | static 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 | |||
186 | static 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 | }; | ||
307 | update_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 | |||
337 | static 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 | |||
357 | static 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 | |||
521 | static 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 | */ | ||
532 | static 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 | |||
549 | static 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 | |||
714 | static 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 | |||
738 | static 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 | |||
764 | module_init(soc_probe); | ||
765 | module_exit(soc_cleanup); | ||
766 | MODULE_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) \ | ||
74 | do { (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 | |||
84 | typedef void __iomem *xram_p; | ||
85 | |||
86 | /* Get 32bit number from XRAM */ | ||
87 | static 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 */ | ||
94 | static inline u32 xram_get_32low(xram_p x) | ||
95 | { | ||
96 | return (u32) sbus_readw(x + 0x02UL); | ||
97 | } | ||
98 | |||
99 | static inline u16 xram_get_16(xram_p x) | ||
100 | { | ||
101 | return sbus_readw(x); | ||
102 | } | ||
103 | |||
104 | static 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 | |||
114 | static 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 | |||
126 | static 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 | |||
138 | static 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 | |||
149 | typedef 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 | |||
169 | typedef struct { | ||
170 | u32 token; | ||
171 | u16 flags; | ||
172 | u8 class; | ||
173 | u8 segcnt; | ||
174 | u32 bytecnt; | ||
175 | } soc_hdr; | ||
176 | |||
177 | typedef 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 | |||
198 | typedef 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 | |||
228 | typedef 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 | |||
252 | struct soc; | ||
253 | |||
254 | typedef struct { | ||
255 | /* This must come first */ | ||
256 | fc_channel fc; | ||
257 | struct soc *s; | ||
258 | u16 flags; | ||
259 | u16 mask; | ||
260 | } soc_port; | ||
261 | |||
262 | typedef 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 | |||
271 | typedef 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 | |||
280 | struct 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 | |||
15 | static 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) | ||
61 | struct socal *socals = NULL; | ||
62 | |||
63 | static 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 | |||
73 | static 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 | ||
85 | static 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 | |||
95 | static 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 | |||
101 | static 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 | |||
111 | static 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 | |||
135 | static 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 | |||
227 | static 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 | |||
244 | static 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 | }; | ||
377 | update_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 | |||
407 | static 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 | |||
437 | static 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 | |||
607 | static 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 | */ | ||
621 | static 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 | |||
658 | static 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 | |||
851 | static 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 | |||
877 | static 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 | |||
904 | module_init(socal_probe); | ||
905 | module_exit(socal_cleanup); | ||
906 | MODULE_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) \ | ||
78 | do { (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 | |||
96 | typedef 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 | |||
117 | typedef struct { | ||
118 | u32 token; | ||
119 | u16 flags; | ||
120 | u8 class; | ||
121 | u8 segcnt; | ||
122 | u32 bytecnt; | ||
123 | } socal_hdr; | ||
124 | |||
125 | typedef 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 | |||
155 | typedef 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 | |||
191 | typedef 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 | |||
204 | typedef 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 | |||
224 | typedef 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 | |||
245 | typedef 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 | |||
273 | struct socal; | ||
274 | |||
275 | typedef struct { | ||
276 | /* This must come first */ | ||
277 | fc_channel fc; | ||
278 | struct socal *s; | ||
279 | u16 flags; | ||
280 | u16 mask; | ||
281 | } socal_port; | ||
282 | |||
283 | typedef 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 | |||
292 | struct 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) */ | ||