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