Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* pluto.c: SparcSTORAGE Array SCSI host adapter driver. |
2 | * | |
3 | * Copyright (C) 1997,1998,1999 Jakub Jelinek (jj@sunsite.mff.cuni.cz) | |
4 | * | |
5 | */ | |
6 | ||
b68a890f | 7 | #include <linux/completion.h> |
1da177e4 LT |
8 | #include <linux/kernel.h> |
9 | #include <linux/delay.h> | |
10 | #include <linux/types.h> | |
11 | #include <linux/string.h> | |
12 | #include <linux/slab.h> | |
13 | #include <linux/blkdev.h> | |
14 | #include <linux/proc_fs.h> | |
15 | #include <linux/stat.h> | |
16 | #include <linux/init.h> | |
1da177e4 LT |
17 | #ifdef CONFIG_KMOD |
18 | #include <linux/kmod.h> | |
19 | #endif | |
20 | ||
21 | #include <asm/irq.h> | |
22 | ||
23 | #include "scsi.h" | |
24 | #include <scsi/scsi_host.h> | |
25 | #include "../fc4/fcp_impl.h" | |
26 | #include "pluto.h" | |
27 | ||
28 | #include <linux/module.h> | |
29 | ||
8d7feac3 CH |
30 | #define RQ_SCSI_BUSY 0xffff |
31 | #define RQ_SCSI_DONE 0xfffe | |
32 | ||
1da177e4 LT |
33 | /* #define PLUTO_DEBUG */ |
34 | ||
35 | #define pluto_printk printk ("PLUTO %s: ", fc->name); printk | |
36 | ||
37 | #ifdef PLUTO_DEBUG | |
38 | #define PLD(x) pluto_printk x; | |
39 | #define PLND(x) printk ("PLUTO: "); printk x; | |
40 | #else | |
41 | #define PLD(x) | |
42 | #define PLND(x) | |
43 | #endif | |
44 | ||
45 | static struct ctrl_inquiry { | |
46 | struct Scsi_Host host; | |
47 | struct pluto pluto; | |
48 | Scsi_Cmnd cmd; | |
49 | char inquiry[256]; | |
50 | fc_channel *fc; | |
0f73832f | 51 | } *fcs __initdata; |
1da177e4 LT |
52 | static int fcscount __initdata = 0; |
53 | static atomic_t fcss __initdata = ATOMIC_INIT(0); | |
b68a890f | 54 | static DECLARE_COMPLETION(fc_detect_complete); |
1da177e4 LT |
55 | |
56 | static int pluto_encode_addr(Scsi_Cmnd *SCpnt, u16 *addr, fc_channel *fc, fcp_cmnd *fcmd); | |
57 | ||
1da177e4 LT |
58 | static void __init pluto_detect_done(Scsi_Cmnd *SCpnt) |
59 | { | |
60 | /* Do nothing */ | |
61 | } | |
62 | ||
63 | static void __init pluto_detect_scsi_done(Scsi_Cmnd *SCpnt) | |
64 | { | |
1da177e4 LT |
65 | PLND(("Detect done %08lx\n", (long)SCpnt)) |
66 | if (atomic_dec_and_test (&fcss)) | |
b68a890f | 67 | complete(&fc_detect_complete); |
1da177e4 LT |
68 | } |
69 | ||
f64a181d | 70 | int pluto_slave_configure(struct scsi_device *device) |
1da177e4 LT |
71 | { |
72 | int depth_to_use; | |
73 | ||
74 | if (device->tagged_supported) | |
75 | depth_to_use = /* 254 */ 8; | |
76 | else | |
77 | depth_to_use = 2; | |
78 | ||
79 | scsi_adjust_queue_depth(device, | |
80 | (device->tagged_supported ? | |
81 | MSG_SIMPLE_TAG : 0), | |
82 | depth_to_use); | |
83 | ||
84 | return 0; | |
85 | } | |
86 | ||
87 | /* Detect all SSAs attached to the machine. | |
88 | To be fast, do it on all online FC channels at the same time. */ | |
d0be4a7d | 89 | int __init pluto_detect(struct scsi_host_template *tpnt) |
1da177e4 LT |
90 | { |
91 | int i, retry, nplutos; | |
92 | fc_channel *fc; | |
f64a181d | 93 | struct scsi_device dev; |
1da177e4 LT |
94 | |
95 | tpnt->proc_name = "pluto"; | |
96 | fcscount = 0; | |
97 | for_each_online_fc_channel(fc) { | |
98 | if (!fc->posmap) | |
99 | fcscount++; | |
100 | } | |
101 | PLND(("%d channels online\n", fcscount)) | |
102 | if (!fcscount) { | |
103 | #if defined(MODULE) && defined(CONFIG_FC4_SOC_MODULE) && defined(CONFIG_KMOD) | |
104 | request_module("soc"); | |
105 | ||
106 | for_each_online_fc_channel(fc) { | |
107 | if (!fc->posmap) | |
108 | fcscount++; | |
109 | } | |
110 | if (!fcscount) | |
111 | #endif | |
112 | return 0; | |
113 | } | |
5cbded58 | 114 | fcs = kmalloc(sizeof (struct ctrl_inquiry) * fcscount, GFP_DMA); |
1da177e4 LT |
115 | if (!fcs) { |
116 | printk ("PLUTO: Not enough memory to probe\n"); | |
117 | return 0; | |
118 | } | |
119 | ||
120 | memset (fcs, 0, sizeof (struct ctrl_inquiry) * fcscount); | |
121 | memset (&dev, 0, sizeof(dev)); | |
122 | atomic_set (&fcss, fcscount); | |
123 | ||
124 | i = 0; | |
125 | for_each_online_fc_channel(fc) { | |
126 | Scsi_Cmnd *SCpnt; | |
127 | struct Scsi_Host *host; | |
128 | struct pluto *pluto; | |
129 | ||
130 | if (i == fcscount) break; | |
131 | if (fc->posmap) continue; | |
132 | ||
133 | PLD(("trying to find SSA\n")) | |
134 | ||
135 | /* If this is already registered to some other SCSI host, then it cannot be pluto */ | |
136 | if (fc->scsi_name[0]) continue; | |
137 | memcpy (fc->scsi_name, "SSA", 4); | |
138 | ||
139 | fcs[i].fc = fc; | |
140 | ||
141 | fc->can_queue = PLUTO_CAN_QUEUE; | |
142 | fc->rsp_size = 64; | |
143 | fc->encode_addr = pluto_encode_addr; | |
144 | ||
145 | fc->fcp_register(fc, TYPE_SCSI_FCP, 0); | |
146 | ||
147 | SCpnt = &(fcs[i].cmd); | |
148 | host = &(fcs[i].host); | |
149 | pluto = (struct pluto *)host->hostdata; | |
150 | ||
151 | pluto->fc = fc; | |
152 | ||
153 | SCpnt->cmnd[0] = INQUIRY; | |
154 | SCpnt->cmnd[4] = 255; | |
155 | ||
156 | /* FC layer requires this, so that SCpnt->device->tagged_supported is initially 0 */ | |
157 | SCpnt->device = &dev; | |
158 | dev.host = host; | |
159 | ||
160 | SCpnt->cmd_len = COMMAND_SIZE(INQUIRY); | |
161 | ||
4aff5e23 | 162 | SCpnt->request->cmd_flags &= ~REQ_STARTED; |
1da177e4 LT |
163 | |
164 | SCpnt->done = pluto_detect_done; | |
1da177e4 LT |
165 | SCpnt->request_bufflen = 256; |
166 | SCpnt->request_buffer = fcs[i].inquiry; | |
167 | PLD(("set up %d %08lx\n", i, (long)SCpnt)) | |
168 | i++; | |
169 | } | |
170 | ||
171 | for (retry = 0; retry < 5; retry++) { | |
172 | for (i = 0; i < fcscount; i++) { | |
173 | if (!fcs[i].fc) break; | |
4aff5e23 JA |
174 | if (!(fcs[i].cmd.request->cmd_flags & REQ_STARTED)) { |
175 | fcs[i].cmd.request->cmd_flags |= REQ_STARTED; | |
1da177e4 LT |
176 | disable_irq(fcs[i].fc->irq); |
177 | PLND(("queuecommand %d %d\n", retry, i)) | |
178 | fcp_scsi_queuecommand (&(fcs[i].cmd), | |
179 | pluto_detect_scsi_done); | |
180 | enable_irq(fcs[i].fc->irq); | |
181 | } | |
182 | } | |
183 | ||
b68a890f | 184 | wait_for_completion_timeout(&fc_detect_complete, 10 * HZ); |
1da177e4 LT |
185 | PLND(("Woken up\n")) |
186 | if (!atomic_read(&fcss)) | |
187 | break; /* All fc channels have answered us */ | |
188 | } | |
1da177e4 LT |
189 | |
190 | PLND(("Finished search\n")) | |
191 | for (i = 0, nplutos = 0; i < fcscount; i++) { | |
192 | Scsi_Cmnd *SCpnt; | |
193 | ||
194 | if (!(fc = fcs[i].fc)) break; | |
195 | ||
196 | SCpnt = &(fcs[i].cmd); | |
197 | ||
198 | /* Let FC mid-level free allocated resources */ | |
199 | SCpnt->done (SCpnt); | |
200 | ||
201 | if (!SCpnt->result) { | |
202 | struct pluto_inquiry *inq; | |
203 | struct pluto *pluto; | |
204 | struct Scsi_Host *host; | |
205 | ||
206 | inq = (struct pluto_inquiry *)fcs[i].inquiry; | |
207 | ||
208 | if ((inq->dtype & 0x1f) == TYPE_PROCESSOR && | |
209 | !strncmp (inq->vendor_id, "SUN", 3) && | |
210 | !strncmp (inq->product_id, "SSA", 3)) { | |
211 | char *p; | |
212 | long *ages; | |
213 | ||
214 | ages = kmalloc (((inq->channels + 1) * inq->targets) * sizeof(long), GFP_KERNEL); | |
215 | if (!ages) continue; | |
216 | ||
217 | host = scsi_register (tpnt, sizeof (struct pluto)); | |
218 | if(!host) | |
219 | { | |
220 | kfree(ages); | |
221 | continue; | |
222 | } | |
223 | ||
224 | if (!try_module_get(fc->module)) { | |
225 | kfree(ages); | |
226 | scsi_unregister(host); | |
227 | continue; | |
228 | } | |
229 | ||
230 | nplutos++; | |
231 | ||
232 | pluto = (struct pluto *)host->hostdata; | |
233 | ||
234 | host->max_id = inq->targets; | |
235 | host->max_channel = inq->channels; | |
236 | host->irq = fc->irq; | |
237 | ||
238 | fc->channels = inq->channels + 1; | |
239 | fc->targets = inq->targets; | |
240 | fc->ages = ages; | |
241 | memset (ages, 0, ((inq->channels + 1) * inq->targets) * sizeof(long)); | |
242 | ||
243 | pluto->fc = fc; | |
244 | memcpy (pluto->rev_str, inq->revision, 4); | |
245 | pluto->rev_str[4] = 0; | |
246 | p = strchr (pluto->rev_str, ' '); | |
247 | if (p) *p = 0; | |
248 | memcpy (pluto->fw_rev_str, inq->fw_revision, 4); | |
249 | pluto->fw_rev_str[4] = 0; | |
250 | p = strchr (pluto->fw_rev_str, ' '); | |
251 | if (p) *p = 0; | |
252 | memcpy (pluto->serial_str, inq->serial, 12); | |
253 | pluto->serial_str[12] = 0; | |
254 | p = strchr (pluto->serial_str, ' '); | |
255 | if (p) *p = 0; | |
256 | ||
257 | PLD(("Found SSA rev %s fw rev %s serial %s %dx%d\n", pluto->rev_str, pluto->fw_rev_str, pluto->serial_str, host->max_channel, host->max_id)) | |
258 | } else | |
259 | fc->fcp_register(fc, TYPE_SCSI_FCP, 1); | |
260 | } else | |
261 | fc->fcp_register(fc, TYPE_SCSI_FCP, 1); | |
262 | } | |
263 | kfree((char *)fcs); | |
264 | if (nplutos) | |
265 | printk ("PLUTO: Total of %d SparcSTORAGE Arrays found\n", nplutos); | |
266 | return nplutos; | |
267 | } | |
268 | ||
269 | int pluto_release(struct Scsi_Host *host) | |
270 | { | |
271 | struct pluto *pluto = (struct pluto *)host->hostdata; | |
272 | fc_channel *fc = pluto->fc; | |
273 | ||
274 | module_put(fc->module); | |
275 | ||
276 | fc->fcp_register(fc, TYPE_SCSI_FCP, 1); | |
277 | PLND((" releasing pluto.\n")); | |
278 | kfree (fc->ages); | |
279 | PLND(("released pluto!\n")); | |
280 | return 0; | |
281 | } | |
282 | ||
283 | const char *pluto_info(struct Scsi_Host *host) | |
284 | { | |
285 | static char buf[128], *p; | |
286 | struct pluto *pluto = (struct pluto *) host->hostdata; | |
287 | ||
288 | sprintf(buf, "SUN SparcSTORAGE Array %s fw %s serial %s %dx%d on %s", | |
289 | pluto->rev_str, pluto->fw_rev_str, pluto->serial_str, | |
290 | host->max_channel, host->max_id, pluto->fc->name); | |
291 | #ifdef __sparc__ | |
292 | p = strchr(buf, 0); | |
293 | sprintf(p, " PROM node %x", pluto->fc->dev->prom_node); | |
294 | #endif | |
295 | return buf; | |
296 | } | |
297 | ||
298 | /* SSA uses this FC4S addressing: | |
299 | switch (addr[0]) | |
300 | { | |
301 | case 0: CONTROLLER - All of addr[1]..addr[3] has to be 0 | |
302 | case 1: SINGLE DISK - addr[1] channel, addr[2] id, addr[3] 0 | |
303 | case 2: DISK GROUP - ??? | |
304 | } | |
305 | ||
306 | So that SCSI mid-layer can access to these, we reserve | |
307 | channel 0 id 0 lun 0 for CONTROLLER | |
308 | and channels 1 .. max_channel are normal single disks. | |
309 | */ | |
310 | static int pluto_encode_addr(Scsi_Cmnd *SCpnt, u16 *addr, fc_channel *fc, fcp_cmnd *fcmd) | |
311 | { | |
312 | PLND(("encode addr %d %d %d\n", SCpnt->device->channel, SCpnt->device->id, SCpnt->cmnd[1] & 0xe0)) | |
313 | /* We don't support LUNs - neither does SSA :) */ | |
314 | if (SCpnt->cmnd[1] & 0xe0) | |
315 | return -EINVAL; | |
316 | if (!SCpnt->device->channel) { | |
317 | if (SCpnt->device->id) | |
318 | return -EINVAL; | |
319 | memset (addr, 0, 4 * sizeof(u16)); | |
320 | } else { | |
321 | addr[0] = 1; | |
322 | addr[1] = SCpnt->device->channel - 1; | |
323 | addr[2] = SCpnt->device->id; | |
324 | addr[3] = 0; | |
325 | } | |
326 | /* We're Point-to-Point, so target it to the default DID */ | |
327 | fcmd->did = fc->did; | |
328 | PLND(("trying %04x%04x%04x%04x\n", addr[0], addr[1], addr[2], addr[3])) | |
329 | return 0; | |
330 | } | |
331 | ||
d0be4a7d | 332 | static struct scsi_host_template driver_template = { |
1da177e4 LT |
333 | .name = "Sparc Storage Array 100/200", |
334 | .detect = pluto_detect, | |
335 | .release = pluto_release, | |
336 | .info = pluto_info, | |
337 | .queuecommand = fcp_scsi_queuecommand, | |
338 | .slave_configure = pluto_slave_configure, | |
339 | .can_queue = PLUTO_CAN_QUEUE, | |
340 | .this_id = -1, | |
341 | .sg_tablesize = 1, | |
342 | .cmd_per_lun = 1, | |
343 | .use_clustering = ENABLE_CLUSTERING, | |
344 | .eh_abort_handler = fcp_scsi_abort, | |
345 | .eh_device_reset_handler = fcp_scsi_dev_reset, | |
1da177e4 LT |
346 | .eh_host_reset_handler = fcp_scsi_host_reset, |
347 | }; | |
348 | ||
349 | #include "scsi_module.c" | |
350 | ||
351 | MODULE_LICENSE("GPL"); | |
352 |