/drivers/net/wan/cycx_drv.c
C | 576 lines | 327 code | 98 blank | 151 comment | 43 complexity | bad84fbfdca99b3a95a0e6225a5789d3 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.0, AGPL-1.0
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 */ 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 64MODULE_AUTHOR("Arnaldo Carvalho de Melo"); 65MODULE_DESCRIPTION("Cyclom 2x Sync Card Driver"); 66MODULE_LICENSE("GPL"); 67 68/* Hardware-specific functions */ 69static int load_cyc2x(struct cycx_hw *hw, struct cycx_firmware *cfm, u32 len); 70static void cycx_bootcfg(struct cycx_hw *hw); 71 72static int reset_cyc2x(void __iomem *addr); 73static int detect_cyc2x(void __iomem *addr); 74 75/* Miscellaneous functions */ 76static int get_option_index(const long *optlist, long optval); 77static 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 */ 84static const char modname[] = "cycx_drv"; 85static const char fullname[] = "Cyclom 2X Support Module"; 86static const 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 */ 93static const 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 100static const 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 111static 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 */ 121static 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 */ 135EXPORT_SYMBOL(cycx_setup); 136int 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 178EXPORT_SYMBOL(cycx_down); 179int cycx_down(struct cycx_hw *hw) 180{ 181 iounmap(hw->dpmbase); 182 return 0; 183} 184 185/* Enable interrupt generation. */ 186static void cycx_inten(struct cycx_hw *hw) 187{ 188 writeb(0, hw->dpmbase); 189} 190 191/* Generate an interrupt to adapter's CPU. */ 192EXPORT_SYMBOL(cycx_intr); 193void 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. */ 201EXPORT_SYMBOL(cycx_exec); 202int 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. */ 219EXPORT_SYMBOL(cycx_peek); 220int 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. */ 232EXPORT_SYMBOL(cycx_poke); 233int 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. */ 248static 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. */ 266static 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 */ 280static 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. */ 289static 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. */ 303static 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. */ 320static 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 length */ 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. */ 351static 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 length */ 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 */ 383static 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, __func__, 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, __func__); 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", __func__, 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 474reset_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. */ 510static 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. */ 522static 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. */ 532static int get_option_index(const 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. */ 544static 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. */ 555static 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 573module_init(cycx_drv_init); 574module_exit(cycx_drv_cleanup); 575 576/* End */