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