Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * cycx_drv.c Cyclom 2X Support Module. | |
3 | * | |
4 | * This module is a library of common hardware specific | |
5 | * functions used by the Cyclades Cyclom 2X sync card. | |
6 | * | |
7 | * Author: Arnaldo Carvalho de Melo <acme@conectiva.com.br> | |
8 | * | |
9 | * Copyright: (c) 1998-2003 Arnaldo Carvalho de Melo | |
10 | * | |
11 | * Based on sdladrv.c by Gene Kozin <genek@compuserve.com> | |
12 | * | |
13 | * This program is free software; you can redistribute it and/or | |
14 | * modify it under the terms of the GNU General Public License | |
15 | * as published by the Free Software Foundation; either version | |
16 | * 2 of the License, or (at your option) any later version. | |
17 | * ============================================================================ | |
18 | * 1999/11/11 acme set_current_state(TASK_INTERRUPTIBLE), code | |
19 | * cleanup | |
20 | * 1999/11/08 acme init_cyc2x deleted, doing nothing | |
21 | * 1999/11/06 acme back to read[bw], write[bw] and memcpy_to and | |
22 | * fromio to use dpmbase ioremaped | |
23 | * 1999/10/26 acme use isa_read[bw], isa_write[bw] & isa_memcpy_to | |
24 | * & fromio | |
25 | * 1999/10/23 acme cleanup to only supports cyclom2x: all the other | |
26 | * boards are no longer manufactured by cyclades, | |
27 | * if someone wants to support them... be my guest! | |
28 | * 1999/05/28 acme cycx_intack & cycx_intde gone for good | |
29 | * 1999/05/18 acme lots of unlogged work, submitting to Linus... | |
30 | * 1999/01/03 acme more judicious use of data types | |
31 | * 1999/01/03 acme judicious use of data types :> | |
32 | * cycx_inten trying to reset pending interrupts | |
33 | * from cyclom 2x - I think this isn't the way to | |
34 | * go, but for now... | |
35 | * 1999/01/02 acme cycx_intack ok, I think there's nothing to do | |
36 | * to ack an int in cycx_drv.c, only handle it in | |
37 | * cyx_isr (or in the other protocols: cyp_isr, | |
38 | * cyf_isr, when they get implemented. | |
39 | * Dec 31, 1998 acme cycx_data_boot & cycx_code_boot fixed, crossing | |
40 | * fingers to see x25_configure in cycx_x25.c | |
41 | * work... :) | |
42 | * Dec 26, 1998 acme load implementation fixed, seems to work! :) | |
43 | * cycx_2x_dpmbase_options with all the possible | |
44 | * DPM addresses (20). | |
45 | * cycx_intr implemented (test this!) | |
46 | * general code cleanup | |
47 | * Dec 8, 1998 Ivan Passos Cyclom-2X firmware load implementation. | |
48 | * Aug 8, 1998 acme Initial version. | |
49 | */ | |
50 | ||
51 | #include <linux/init.h> /* __init */ | |
52 | #include <linux/module.h> | |
53 | #include <linux/kernel.h> /* printk(), and other useful stuff */ | |
54 | #include <linux/stddef.h> /* offsetof(), etc. */ | |
55 | #include <linux/errno.h> /* return codes */ | |
1da177e4 LT |
56 | #include <linux/cycx_drv.h> /* API definitions */ |
57 | #include <linux/cycx_cfm.h> /* CYCX firmware module definitions */ | |
a51d7440 | 58 | #include <linux/delay.h> /* udelay, msleep_interruptible */ |
1da177e4 LT |
59 | #include <asm/io.h> /* read[wl], write[wl], ioremap, iounmap */ |
60 | ||
61 | #define MOD_VERSION 0 | |
62 | #define MOD_RELEASE 6 | |
63 | ||
64 | MODULE_AUTHOR("Arnaldo Carvalho de Melo"); | |
65 | MODULE_DESCRIPTION("Cyclom 2x Sync Card Driver"); | |
66 | MODULE_LICENSE("GPL"); | |
67 | ||
68 | /* Hardware-specific functions */ | |
69 | static int load_cyc2x(struct cycx_hw *hw, struct cycx_firmware *cfm, u32 len); | |
70 | static void cycx_bootcfg(struct cycx_hw *hw); | |
71 | ||
72 | static int reset_cyc2x(void __iomem *addr); | |
73 | static int detect_cyc2x(void __iomem *addr); | |
74 | ||
75 | /* Miscellaneous functions */ | |
1da177e4 LT |
76 | static int get_option_index(long *optlist, long optval); |
77 | static u16 checksum(u8 *buf, u32 len); | |
78 | ||
79 | #define wait_cyc(addr) cycx_exec(addr + CMD_OFFSET) | |
80 | ||
81 | /* Global Data */ | |
82 | ||
83 | /* private data */ | |
84 | static char modname[] = "cycx_drv"; | |
85 | static char fullname[] = "Cyclom 2X Support Module"; | |
86 | static char copyright[] = "(c) 1998-2003 Arnaldo Carvalho de Melo " | |
87 | "<acme@conectiva.com.br>"; | |
88 | ||
89 | /* Hardware configuration options. | |
90 | * These are arrays of configuration options used by verification routines. | |
91 | * The first element of each array is its size (i.e. number of options). | |
92 | */ | |
93 | static long cyc2x_dpmbase_options[] = { | |
94 | 20, | |
95 | 0xA0000, 0xA4000, 0xA8000, 0xAC000, 0xB0000, 0xB4000, 0xB8000, | |
96 | 0xBC000, 0xC0000, 0xC4000, 0xC8000, 0xCC000, 0xD0000, 0xD4000, | |
97 | 0xD8000, 0xDC000, 0xE0000, 0xE4000, 0xE8000, 0xEC000 | |
98 | }; | |
99 | ||
100 | static long cycx_2x_irq_options[] = { 7, 3, 5, 9, 10, 11, 12, 15 }; | |
101 | ||
102 | /* Kernel Loadable Module Entry Points */ | |
103 | /* Module 'insert' entry point. | |
104 | * o print announcement | |
105 | * o initialize static data | |
106 | * | |
107 | * Return: 0 Ok | |
108 | * < 0 error. | |
109 | * Context: process */ | |
110 | ||
7665a089 | 111 | static int __init cycx_drv_init(void) |
1da177e4 LT |
112 | { |
113 | printk(KERN_INFO "%s v%u.%u %s\n", fullname, MOD_VERSION, MOD_RELEASE, | |
114 | copyright); | |
115 | ||
116 | return 0; | |
117 | } | |
118 | ||
119 | /* Module 'remove' entry point. | |
120 | * o release all remaining system resources */ | |
7665a089 | 121 | static void cycx_drv_cleanup(void) |
1da177e4 LT |
122 | { |
123 | } | |
124 | ||
125 | /* Kernel APIs */ | |
126 | /* Set up adapter. | |
127 | * o detect adapter type | |
128 | * o verify hardware configuration options | |
129 | * o check for hardware conflicts | |
130 | * o set up adapter shared memory | |
131 | * o test adapter memory | |
132 | * o load firmware | |
133 | * Return: 0 ok. | |
134 | * < 0 error */ | |
135 | EXPORT_SYMBOL(cycx_setup); | |
136 | int cycx_setup(struct cycx_hw *hw, void *cfm, u32 len, unsigned long dpmbase) | |
137 | { | |
138 | int err; | |
139 | ||
140 | /* Verify IRQ configuration options */ | |
141 | if (!get_option_index(cycx_2x_irq_options, hw->irq)) { | |
142 | printk(KERN_ERR "%s: IRQ %d is invalid!\n", modname, hw->irq); | |
143 | return -EINVAL; | |
144 | } | |
145 | ||
146 | /* Setup adapter dual-port memory window and test memory */ | |
147 | if (!dpmbase) { | |
148 | printk(KERN_ERR "%s: you must specify the dpm address!\n", | |
149 | modname); | |
150 | return -EINVAL; | |
151 | } else if (!get_option_index(cyc2x_dpmbase_options, dpmbase)) { | |
152 | printk(KERN_ERR "%s: memory address 0x%lX is invalid!\n", | |
153 | modname, dpmbase); | |
154 | return -EINVAL; | |
155 | } | |
156 | ||
157 | hw->dpmbase = ioremap(dpmbase, CYCX_WINDOWSIZE); | |
158 | hw->dpmsize = CYCX_WINDOWSIZE; | |
159 | ||
160 | if (!detect_cyc2x(hw->dpmbase)) { | |
161 | printk(KERN_ERR "%s: adapter Cyclom 2X not found at " | |
162 | "address 0x%lX!\n", modname, dpmbase); | |
163 | return -EINVAL; | |
164 | } | |
165 | ||
166 | printk(KERN_INFO "%s: found Cyclom 2X card at address 0x%lX.\n", | |
167 | modname, dpmbase); | |
168 | ||
169 | /* Load firmware. If loader fails then shut down adapter */ | |
170 | err = load_cyc2x(hw, cfm, len); | |
171 | ||
172 | if (err) | |
173 | cycx_down(hw); /* shutdown adapter */ | |
174 | ||
175 | return err; | |
176 | } | |
177 | ||
178 | EXPORT_SYMBOL(cycx_down); | |
179 | int cycx_down(struct cycx_hw *hw) | |
180 | { | |
181 | iounmap(hw->dpmbase); | |
182 | return 0; | |
183 | } | |
184 | ||
185 | /* Enable interrupt generation. */ | |
7665a089 | 186 | static void cycx_inten(struct cycx_hw *hw) |
1da177e4 LT |
187 | { |
188 | writeb(0, hw->dpmbase); | |
189 | } | |
190 | ||
191 | /* Generate an interrupt to adapter's CPU. */ | |
192 | EXPORT_SYMBOL(cycx_intr); | |
193 | void cycx_intr(struct cycx_hw *hw) | |
194 | { | |
195 | writew(0, hw->dpmbase + GEN_CYCX_INTR); | |
196 | } | |
197 | ||
198 | /* Execute Adapter Command. | |
199 | * o Set exec flag. | |
200 | * o Busy-wait until flag is reset. */ | |
201 | EXPORT_SYMBOL(cycx_exec); | |
202 | int cycx_exec(void __iomem *addr) | |
203 | { | |
204 | u16 i = 0; | |
205 | /* wait till addr content is zeroed */ | |
206 | ||
207 | while (readw(addr)) { | |
208 | udelay(1000); | |
209 | ||
210 | if (++i > 50) | |
211 | return -1; | |
212 | } | |
213 | ||
214 | return 0; | |
215 | } | |
216 | ||
217 | /* Read absolute adapter memory. | |
218 | * Transfer data from adapter's memory to data buffer. */ | |
219 | EXPORT_SYMBOL(cycx_peek); | |
220 | int cycx_peek(struct cycx_hw *hw, u32 addr, void *buf, u32 len) | |
221 | { | |
222 | if (len == 1) | |
223 | *(u8*)buf = readb(hw->dpmbase + addr); | |
224 | else | |
225 | memcpy_fromio(buf, hw->dpmbase + addr, len); | |
226 | ||
227 | return 0; | |
228 | } | |
229 | ||
230 | /* Write Absolute Adapter Memory. | |
231 | * Transfer data from data buffer to adapter's memory. */ | |
232 | EXPORT_SYMBOL(cycx_poke); | |
233 | int cycx_poke(struct cycx_hw *hw, u32 addr, void *buf, u32 len) | |
234 | { | |
235 | if (len == 1) | |
236 | writeb(*(u8*)buf, hw->dpmbase + addr); | |
237 | else | |
238 | memcpy_toio(hw->dpmbase + addr, buf, len); | |
239 | ||
240 | return 0; | |
241 | } | |
242 | ||
243 | /* Hardware-Specific Functions */ | |
244 | ||
245 | /* Load Aux Routines */ | |
246 | /* Reset board hardware. | |
247 | return 1 if memory exists at addr and 0 if not. */ | |
248 | static int memory_exists(void __iomem *addr) | |
249 | { | |
250 | int tries = 0; | |
251 | ||
252 | for (; tries < 3 ; tries++) { | |
253 | writew(TEST_PATTERN, addr + 0x10); | |
254 | ||
255 | if (readw(addr + 0x10) == TEST_PATTERN) | |
256 | if (readw(addr + 0x10) == TEST_PATTERN) | |
257 | return 1; | |
258 | ||
a51d7440 | 259 | msleep_interruptible(1 * 1000); |
1da177e4 LT |
260 | } |
261 | ||
262 | return 0; | |
263 | } | |
264 | ||
265 | /* Load reset code. */ | |
266 | static void reset_load(void __iomem *addr, u8 *buffer, u32 cnt) | |
267 | { | |
268 | void __iomem *pt_code = addr + RESET_OFFSET; | |
269 | u16 i; /*, j; */ | |
270 | ||
271 | for (i = 0 ; i < cnt ; i++) { | |
272 | /* for (j = 0 ; j < 50 ; j++); Delay - FIXME busy waiting... */ | |
273 | writeb(*buffer++, pt_code++); | |
274 | } | |
275 | } | |
276 | ||
277 | /* Load buffer using boot interface. | |
278 | * o copy data from buffer to Cyclom-X memory | |
279 | * o wait for reset code to copy it to right portion of memory */ | |
280 | static int buffer_load(void __iomem *addr, u8 *buffer, u32 cnt) | |
281 | { | |
282 | memcpy_toio(addr + DATA_OFFSET, buffer, cnt); | |
283 | writew(GEN_BOOT_DAT, addr + CMD_OFFSET); | |
284 | ||
285 | return wait_cyc(addr); | |
286 | } | |
287 | ||
288 | /* Set up entry point and kick start Cyclom-X CPU. */ | |
289 | static void cycx_start(void __iomem *addr) | |
290 | { | |
291 | /* put in 0x30 offset the jump instruction to the code entry point */ | |
292 | writeb(0xea, addr + 0x30); | |
293 | writeb(0x00, addr + 0x31); | |
294 | writeb(0xc4, addr + 0x32); | |
295 | writeb(0x00, addr + 0x33); | |
296 | writeb(0x00, addr + 0x34); | |
297 | ||
298 | /* cmd to start executing code */ | |
299 | writew(GEN_START, addr + CMD_OFFSET); | |
300 | } | |
301 | ||
302 | /* Load and boot reset code. */ | |
303 | static void cycx_reset_boot(void __iomem *addr, u8 *code, u32 len) | |
304 | { | |
305 | void __iomem *pt_start = addr + START_OFFSET; | |
306 | ||
307 | writeb(0xea, pt_start++); /* jmp to f000:3f00 */ | |
308 | writeb(0x00, pt_start++); | |
309 | writeb(0xfc, pt_start++); | |
310 | writeb(0x00, pt_start++); | |
311 | writeb(0xf0, pt_start); | |
312 | reset_load(addr, code, len); | |
313 | ||
314 | /* 80186 was in hold, go */ | |
315 | writeb(0, addr + START_CPU); | |
a51d7440 | 316 | msleep_interruptible(1 * 1000); |
1da177e4 LT |
317 | } |
318 | ||
319 | /* Load data.bin file through boot (reset) interface. */ | |
320 | static int cycx_data_boot(void __iomem *addr, u8 *code, u32 len) | |
321 | { | |
322 | void __iomem *pt_boot_cmd = addr + CMD_OFFSET; | |
323 | u32 i; | |
324 | ||
efad798b | 325 | /* boot buffer length */ |
1da177e4 LT |
326 | writew(CFM_LOAD_BUFSZ, pt_boot_cmd + sizeof(u16)); |
327 | writew(GEN_DEFPAR, pt_boot_cmd); | |
328 | ||
329 | if (wait_cyc(addr) < 0) | |
330 | return -1; | |
331 | ||
332 | writew(0, pt_boot_cmd + sizeof(u16)); | |
333 | writew(0x4000, pt_boot_cmd + 2 * sizeof(u16)); | |
334 | writew(GEN_SET_SEG, pt_boot_cmd); | |
335 | ||
336 | if (wait_cyc(addr) < 0) | |
337 | return -1; | |
338 | ||
339 | for (i = 0 ; i < len ; i += CFM_LOAD_BUFSZ) | |
340 | if (buffer_load(addr, code + i, | |
341 | min_t(u32, CFM_LOAD_BUFSZ, (len - i))) < 0) { | |
342 | printk(KERN_ERR "%s: Error !!\n", modname); | |
343 | return -1; | |
344 | } | |
345 | ||
346 | return 0; | |
347 | } | |
348 | ||
349 | ||
350 | /* Load code.bin file through boot (reset) interface. */ | |
351 | static int cycx_code_boot(void __iomem *addr, u8 *code, u32 len) | |
352 | { | |
353 | void __iomem *pt_boot_cmd = addr + CMD_OFFSET; | |
354 | u32 i; | |
355 | ||
efad798b | 356 | /* boot buffer length */ |
1da177e4 LT |
357 | writew(CFM_LOAD_BUFSZ, pt_boot_cmd + sizeof(u16)); |
358 | writew(GEN_DEFPAR, pt_boot_cmd); | |
359 | ||
360 | if (wait_cyc(addr) < 0) | |
361 | return -1; | |
362 | ||
363 | writew(0x0000, pt_boot_cmd + sizeof(u16)); | |
364 | writew(0xc400, pt_boot_cmd + 2 * sizeof(u16)); | |
365 | writew(GEN_SET_SEG, pt_boot_cmd); | |
366 | ||
367 | if (wait_cyc(addr) < 0) | |
368 | return -1; | |
369 | ||
370 | for (i = 0 ; i < len ; i += CFM_LOAD_BUFSZ) | |
371 | if (buffer_load(addr, code + i, | |
372 | min_t(u32, CFM_LOAD_BUFSZ, (len - i)))) { | |
373 | printk(KERN_ERR "%s: Error !!\n", modname); | |
374 | return -1; | |
375 | } | |
376 | ||
377 | return 0; | |
378 | } | |
379 | ||
380 | /* Load adapter from the memory image of the CYCX firmware module. | |
381 | * o verify firmware integrity and compatibility | |
382 | * o start adapter up */ | |
383 | static int load_cyc2x(struct cycx_hw *hw, struct cycx_firmware *cfm, u32 len) | |
384 | { | |
385 | int i, j; | |
386 | struct cycx_fw_header *img_hdr; | |
387 | u8 *reset_image, | |
388 | *data_image, | |
389 | *code_image; | |
390 | void __iomem *pt_cycld = hw->dpmbase + 0x400; | |
391 | u16 cksum; | |
392 | ||
393 | /* Announce */ | |
394 | printk(KERN_INFO "%s: firmware signature=\"%s\"\n", modname, | |
395 | cfm->signature); | |
396 | ||
397 | /* Verify firmware signature */ | |
398 | if (strcmp(cfm->signature, CFM_SIGNATURE)) { | |
399 | printk(KERN_ERR "%s:load_cyc2x: not Cyclom-2X firmware!\n", | |
400 | modname); | |
401 | return -EINVAL; | |
402 | } | |
403 | ||
404 | printk(KERN_INFO "%s: firmware version=%u\n", modname, cfm->version); | |
405 | ||
406 | /* Verify firmware module format version */ | |
407 | if (cfm->version != CFM_VERSION) { | |
408 | printk(KERN_ERR "%s:%s: firmware format %u rejected! " | |
409 | "Expecting %u.\n", | |
b39d66a8 | 410 | modname, __func__, cfm->version, CFM_VERSION); |
1da177e4 LT |
411 | return -EINVAL; |
412 | } | |
413 | ||
414 | /* Verify firmware module length and checksum */ | |
415 | cksum = checksum((u8*)&cfm->info, sizeof(struct cycx_fw_info) + | |
416 | cfm->info.codesize); | |
417 | /* | |
418 | FIXME cfm->info.codesize is off by 2 | |
419 | if (((len - sizeof(struct cycx_firmware) - 1) != cfm->info.codesize) || | |
420 | */ | |
421 | if (cksum != cfm->checksum) { | |
422 | printk(KERN_ERR "%s:%s: firmware corrupted!\n", | |
b39d66a8 | 423 | modname, __func__); |
1da177e4 LT |
424 | printk(KERN_ERR " cdsize = 0x%x (expected 0x%lx)\n", |
425 | len - (int)sizeof(struct cycx_firmware) - 1, | |
426 | cfm->info.codesize); | |
427 | printk(KERN_ERR " chksum = 0x%x (expected 0x%x)\n", | |
428 | cksum, cfm->checksum); | |
429 | return -EINVAL; | |
430 | } | |
431 | ||
432 | /* If everything is ok, set reset, data and code pointers */ | |
433 | img_hdr = (struct cycx_fw_header *)&cfm->image; | |
434 | #ifdef FIRMWARE_DEBUG | |
b39d66a8 | 435 | printk(KERN_INFO "%s:%s: image sizes\n", __func__, modname); |
1da177e4 LT |
436 | printk(KERN_INFO " reset=%lu\n", img_hdr->reset_size); |
437 | printk(KERN_INFO " data=%lu\n", img_hdr->data_size); | |
438 | printk(KERN_INFO " code=%lu\n", img_hdr->code_size); | |
439 | #endif | |
440 | reset_image = ((u8 *)img_hdr) + sizeof(struct cycx_fw_header); | |
441 | data_image = reset_image + img_hdr->reset_size; | |
442 | code_image = data_image + img_hdr->data_size; | |
443 | ||
444 | /*---- Start load ----*/ | |
445 | /* Announce */ | |
446 | printk(KERN_INFO "%s: loading firmware %s (ID=%u)...\n", modname, | |
447 | cfm->descr[0] ? cfm->descr : "unknown firmware", | |
448 | cfm->info.codeid); | |
449 | ||
450 | for (i = 0 ; i < 5 ; i++) { | |
451 | /* Reset Cyclom hardware */ | |
452 | if (!reset_cyc2x(hw->dpmbase)) { | |
453 | printk(KERN_ERR "%s: dpm problem or board not found\n", | |
454 | modname); | |
455 | return -EINVAL; | |
456 | } | |
457 | ||
458 | /* Load reset.bin */ | |
459 | cycx_reset_boot(hw->dpmbase, reset_image, img_hdr->reset_size); | |
460 | /* reset is waiting for boot */ | |
461 | writew(GEN_POWER_ON, pt_cycld); | |
a51d7440 | 462 | msleep_interruptible(1 * 1000); |
1da177e4 LT |
463 | |
464 | for (j = 0 ; j < 3 ; j++) | |
465 | if (!readw(pt_cycld)) | |
466 | goto reset_loaded; | |
467 | else | |
a51d7440 | 468 | msleep_interruptible(1 * 1000); |
1da177e4 LT |
469 | } |
470 | ||
471 | printk(KERN_ERR "%s: reset not started.\n", modname); | |
472 | return -EINVAL; | |
473 | ||
474 | reset_loaded: | |
475 | /* Load data.bin */ | |
476 | if (cycx_data_boot(hw->dpmbase, data_image, img_hdr->data_size)) { | |
477 | printk(KERN_ERR "%s: cannot load data file.\n", modname); | |
478 | return -EINVAL; | |
479 | } | |
480 | ||
481 | /* Load code.bin */ | |
482 | if (cycx_code_boot(hw->dpmbase, code_image, img_hdr->code_size)) { | |
483 | printk(KERN_ERR "%s: cannot load code file.\n", modname); | |
484 | return -EINVAL; | |
485 | } | |
486 | ||
487 | /* Prepare boot-time configuration data */ | |
488 | cycx_bootcfg(hw); | |
489 | ||
490 | /* kick-off CPU */ | |
491 | cycx_start(hw->dpmbase); | |
492 | ||
493 | /* Arthur Ganzert's tip: wait a while after the firmware loading... | |
494 | seg abr 26 17:17:12 EST 1999 - acme */ | |
a51d7440 | 495 | msleep_interruptible(7 * 1000); |
1da177e4 LT |
496 | printk(KERN_INFO "%s: firmware loaded!\n", modname); |
497 | ||
498 | /* enable interrupts */ | |
499 | cycx_inten(hw); | |
500 | ||
501 | return 0; | |
502 | } | |
503 | ||
504 | /* Prepare boot-time firmware configuration data. | |
505 | * o initialize configuration data area | |
506 | From async.doc - V_3.4.0 - 07/18/1994 | |
507 | - As of now, only static buffers are available to the user. | |
508 | So, the bit VD_RXDIRC must be set in 'valid'. That means that user | |
509 | wants to use the static transmission and reception buffers. */ | |
510 | static void cycx_bootcfg(struct cycx_hw *hw) | |
511 | { | |
512 | /* use fixed buffers */ | |
513 | writeb(FIXED_BUFFERS, hw->dpmbase + CONF_OFFSET); | |
514 | } | |
515 | ||
516 | /* Detect Cyclom 2x adapter. | |
517 | * Following tests are used to detect Cyclom 2x adapter: | |
518 | * to be completed based on the tests done below | |
519 | * Return 1 if detected o.k. or 0 if failed. | |
520 | * Note: This test is destructive! Adapter will be left in shutdown | |
521 | * state after the test. */ | |
522 | static int detect_cyc2x(void __iomem *addr) | |
523 | { | |
524 | reset_cyc2x(addr); | |
525 | ||
526 | return memory_exists(addr); | |
527 | } | |
528 | ||
529 | /* Miscellaneous */ | |
530 | /* Get option's index into the options list. | |
531 | * Return option's index (1 .. N) or zero if option is invalid. */ | |
532 | static int get_option_index(long *optlist, long optval) | |
533 | { | |
534 | int i = 1; | |
535 | ||
536 | for (; i <= optlist[0]; ++i) | |
537 | if (optlist[i] == optval) | |
538 | return i; | |
539 | ||
540 | return 0; | |
541 | } | |
542 | ||
543 | /* Reset adapter's CPU. */ | |
544 | static int reset_cyc2x(void __iomem *addr) | |
545 | { | |
546 | writeb(0, addr + RST_ENABLE); | |
a51d7440 | 547 | msleep_interruptible(2 * 1000); |
1da177e4 | 548 | writeb(0, addr + RST_DISABLE); |
a51d7440 | 549 | msleep_interruptible(2 * 1000); |
1da177e4 LT |
550 | |
551 | return memory_exists(addr); | |
552 | } | |
553 | ||
1da177e4 LT |
554 | /* Calculate 16-bit CRC using CCITT polynomial. */ |
555 | static u16 checksum(u8 *buf, u32 len) | |
556 | { | |
557 | u16 crc = 0; | |
558 | u16 mask, flag; | |
559 | ||
560 | for (; len; --len, ++buf) | |
561 | for (mask = 0x80; mask; mask >>= 1) { | |
562 | flag = (crc & 0x8000); | |
563 | crc <<= 1; | |
564 | crc |= ((*buf & mask) ? 1 : 0); | |
565 | ||
566 | if (flag) | |
567 | crc ^= 0x1021; | |
568 | } | |
569 | ||
570 | return crc; | |
571 | } | |
572 | ||
573 | module_init(cycx_drv_init); | |
574 | module_exit(cycx_drv_cleanup); | |
575 | ||
576 | /* End */ |