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> | |
16 | #include <linux/config.h> | |
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 | ||
30 | /* #define PLUTO_DEBUG */ | |
31 | ||
32 | #define pluto_printk printk ("PLUTO %s: ", fc->name); printk | |
33 | ||
34 | #ifdef PLUTO_DEBUG | |
35 | #define PLD(x) pluto_printk x; | |
36 | #define PLND(x) printk ("PLUTO: "); printk x; | |
37 | #else | |
38 | #define PLD(x) | |
39 | #define PLND(x) | |
40 | #endif | |
41 | ||
42 | static struct ctrl_inquiry { | |
43 | struct Scsi_Host host; | |
44 | struct pluto pluto; | |
45 | Scsi_Cmnd cmd; | |
46 | char inquiry[256]; | |
47 | fc_channel *fc; | |
0f73832f | 48 | } *fcs __initdata; |
1da177e4 LT |
49 | static int fcscount __initdata = 0; |
50 | static atomic_t fcss __initdata = ATOMIC_INIT(0); | |
51 | DECLARE_MUTEX_LOCKED(fc_sem); | |
52 | ||
53 | static int pluto_encode_addr(Scsi_Cmnd *SCpnt, u16 *addr, fc_channel *fc, fcp_cmnd *fcmd); | |
54 | ||
55 | static void __init pluto_detect_timeout(unsigned long data) | |
56 | { | |
57 | PLND(("Timeout\n")) | |
58 | up(&fc_sem); | |
59 | } | |
60 | ||
61 | static void __init pluto_detect_done(Scsi_Cmnd *SCpnt) | |
62 | { | |
63 | /* Do nothing */ | |
64 | } | |
65 | ||
66 | static void __init pluto_detect_scsi_done(Scsi_Cmnd *SCpnt) | |
67 | { | |
68 | SCpnt->request->rq_status = RQ_SCSI_DONE; | |
69 | PLND(("Detect done %08lx\n", (long)SCpnt)) | |
70 | if (atomic_dec_and_test (&fcss)) | |
71 | up(&fc_sem); | |
72 | } | |
73 | ||
f64a181d | 74 | int pluto_slave_configure(struct scsi_device *device) |
1da177e4 LT |
75 | { |
76 | int depth_to_use; | |
77 | ||
78 | if (device->tagged_supported) | |
79 | depth_to_use = /* 254 */ 8; | |
80 | else | |
81 | depth_to_use = 2; | |
82 | ||
83 | scsi_adjust_queue_depth(device, | |
84 | (device->tagged_supported ? | |
85 | MSG_SIMPLE_TAG : 0), | |
86 | depth_to_use); | |
87 | ||
88 | return 0; | |
89 | } | |
90 | ||
91 | /* Detect all SSAs attached to the machine. | |
92 | To be fast, do it on all online FC channels at the same time. */ | |
d0be4a7d | 93 | int __init pluto_detect(struct scsi_host_template *tpnt) |
1da177e4 LT |
94 | { |
95 | int i, retry, nplutos; | |
96 | fc_channel *fc; | |
f64a181d | 97 | struct scsi_device dev; |
8d06afab | 98 | DEFINE_TIMER(fc_timer, pluto_detect_timeout, 0, 0); |
1da177e4 LT |
99 | |
100 | tpnt->proc_name = "pluto"; | |
101 | fcscount = 0; | |
102 | for_each_online_fc_channel(fc) { | |
103 | if (!fc->posmap) | |
104 | fcscount++; | |
105 | } | |
106 | PLND(("%d channels online\n", fcscount)) | |
107 | if (!fcscount) { | |
108 | #if defined(MODULE) && defined(CONFIG_FC4_SOC_MODULE) && defined(CONFIG_KMOD) | |
109 | request_module("soc"); | |
110 | ||
111 | for_each_online_fc_channel(fc) { | |
112 | if (!fc->posmap) | |
113 | fcscount++; | |
114 | } | |
115 | if (!fcscount) | |
116 | #endif | |
117 | return 0; | |
118 | } | |
119 | fcs = (struct ctrl_inquiry *) kmalloc (sizeof (struct ctrl_inquiry) * fcscount, GFP_DMA); | |
120 | if (!fcs) { | |
121 | printk ("PLUTO: Not enough memory to probe\n"); | |
122 | return 0; | |
123 | } | |
124 | ||
125 | memset (fcs, 0, sizeof (struct ctrl_inquiry) * fcscount); | |
126 | memset (&dev, 0, sizeof(dev)); | |
127 | atomic_set (&fcss, fcscount); | |
128 | ||
129 | i = 0; | |
130 | for_each_online_fc_channel(fc) { | |
131 | Scsi_Cmnd *SCpnt; | |
132 | struct Scsi_Host *host; | |
133 | struct pluto *pluto; | |
134 | ||
135 | if (i == fcscount) break; | |
136 | if (fc->posmap) continue; | |
137 | ||
138 | PLD(("trying to find SSA\n")) | |
139 | ||
140 | /* If this is already registered to some other SCSI host, then it cannot be pluto */ | |
141 | if (fc->scsi_name[0]) continue; | |
142 | memcpy (fc->scsi_name, "SSA", 4); | |
143 | ||
144 | fcs[i].fc = fc; | |
145 | ||
146 | fc->can_queue = PLUTO_CAN_QUEUE; | |
147 | fc->rsp_size = 64; | |
148 | fc->encode_addr = pluto_encode_addr; | |
149 | ||
150 | fc->fcp_register(fc, TYPE_SCSI_FCP, 0); | |
151 | ||
152 | SCpnt = &(fcs[i].cmd); | |
153 | host = &(fcs[i].host); | |
154 | pluto = (struct pluto *)host->hostdata; | |
155 | ||
156 | pluto->fc = fc; | |
157 | ||
158 | SCpnt->cmnd[0] = INQUIRY; | |
159 | SCpnt->cmnd[4] = 255; | |
160 | ||
161 | /* FC layer requires this, so that SCpnt->device->tagged_supported is initially 0 */ | |
162 | SCpnt->device = &dev; | |
163 | dev.host = host; | |
164 | ||
165 | SCpnt->cmd_len = COMMAND_SIZE(INQUIRY); | |
166 | ||
167 | SCpnt->request->rq_status = RQ_SCSI_BUSY; | |
168 | ||
169 | SCpnt->done = pluto_detect_done; | |
170 | SCpnt->bufflen = 256; | |
171 | SCpnt->buffer = fcs[i].inquiry; | |
172 | SCpnt->request_bufflen = 256; | |
173 | SCpnt->request_buffer = fcs[i].inquiry; | |
174 | PLD(("set up %d %08lx\n", i, (long)SCpnt)) | |
175 | i++; | |
176 | } | |
177 | ||
178 | for (retry = 0; retry < 5; retry++) { | |
179 | for (i = 0; i < fcscount; i++) { | |
180 | if (!fcs[i].fc) break; | |
181 | if (fcs[i].cmd.request->rq_status != RQ_SCSI_DONE) { | |
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 |