OpenCores
URL https://opencores.org/ocsvn/test_project/test_project/trunk

Subversion Repositories test_project

[/] [test_project/] [trunk/] [linux_sd_driver/] [drivers/] [net/] [wan/] [cycx_drv.c] - Blame information for rev 62

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 62 marcus.erl
/*
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 */
56
#include <linux/cycx_drv.h>     /* API definitions */
57
#include <linux/cycx_cfm.h>     /* CYCX firmware module definitions */
58
#include <linux/delay.h>        /* udelay, msleep_interruptible */
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 */
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
 
111
static int __init cycx_drv_init(void)
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 */
121
static void cycx_drv_cleanup(void)
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.  */
186
static void cycx_inten(struct cycx_hw *hw)
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
 
259
                msleep_interruptible(1 * 1000);
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);
316
        msleep_interruptible(1 * 1000);
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
 
325
        /* boot buffer lenght */
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
 
356
        /* boot buffer lenght */
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",
410
                                modname, __FUNCTION__, cfm->version, CFM_VERSION);
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",
423
                                modname, __FUNCTION__);
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
435
        printk(KERN_INFO "%s:%s: image sizes\n", __FUNCTION__, modname);
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);
462
                msleep_interruptible(1 * 1000);
463
 
464
                for (j = 0 ; j < 3 ; j++)
465
                        if (!readw(pt_cycld))
466
                                goto reset_loaded;
467
                        else
468
                                msleep_interruptible(1 * 1000);
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 */
495
        msleep_interruptible(7 * 1000);
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);
547
        msleep_interruptible(2 * 1000);
548
        writeb(0, addr + RST_DISABLE);
549
        msleep_interruptible(2 * 1000);
550
 
551
        return memory_exists(addr);
552
}
553
 
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 */

powered by: WebSVN 2.1.0

© copyright 1999-2025 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.