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(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 char modname[] = "cycx_drv"; 85static char fullname[] = "Cyclom 2X Support Module"; 86static 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 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 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 writeb(*buffer++, pt_code++); 273 } 274} 275 276/* Load buffer using boot interface. 277 * o copy data from buffer to Cyclom-X memory 278 * o wait for reset code to copy it to right portion of memory */ 279static int buffer_load(void __iomem *addr, u8 *buffer, u32 cnt) 280{ 281 memcpy_toio(addr + DATA_OFFSET, buffer, cnt); 282 writew(GEN_BOOT_DAT, addr + CMD_OFFSET); 283 284 return wait_cyc(addr); 285} 286 287/* Set up entry point and kick start Cyclom-X CPU. */ 288static void cycx_start(void __iomem *addr) 289{ 290 /* put in 0x30 offset the jump instruction to the code entry point */ 291 writeb(0xea, addr + 0x30); 292 writeb(0x00, addr + 0x31); 293 writeb(0xc4, addr + 0x32); 294 writeb(0x00, addr + 0x33); 295 writeb(0x00, addr + 0x34); 296 297 /* cmd to start executing code */ 298 writew(GEN_START, addr + CMD_OFFSET); 299} 300 301/* Load and boot reset code. */ 302static void cycx_reset_boot(void __iomem *addr, u8 *code, u32 len) 303{ 304 void __iomem *pt_start = addr + START_OFFSET; 305 306 writeb(0xea, pt_start++); /* jmp to f000:3f00 */ 307 writeb(0x00, pt_start++); 308 writeb(0xfc, pt_start++); 309 writeb(0x00, pt_start++); 310 writeb(0xf0, pt_start); 311 reset_load(addr, code, len); 312 313 /* 80186 was in hold, go */ 314 writeb(0, addr + START_CPU); 315 msleep_interruptible(1 * 1000); 316} 317 318/* Load data.bin file through boot (reset) interface. */ 319static int cycx_data_boot(void __iomem *addr, u8 *code, u32 len) 320{ 321 void __iomem *pt_boot_cmd = addr + CMD_OFFSET; 322 u32 i; 323 324 /* boot buffer length */ 325 writew(CFM_LOAD_BUFSZ, pt_boot_cmd + sizeof(u16)); 326 writew(GEN_DEFPAR, pt_boot_cmd); 327 328 if (wait_cyc(addr) < 0) 329 return -1; 330 331 writew(0, pt_boot_cmd + sizeof(u16)); 332 writew(0x4000, pt_boot_cmd + 2 * sizeof(u16)); 333 writew(GEN_SET_SEG, pt_boot_cmd); 334 335 if (wait_cyc(addr) < 0) 336 return -1; 337 338 for (i = 0 ; i < len ; i += CFM_LOAD_BUFSZ) 339 if (buffer_load(addr, code + i, 340 min_t(u32, CFM_LOAD_BUFSZ, (len - i))) < 0) { 341 printk(KERN_ERR "%s: Error !!\n", modname); 342 return -1; 343 } 344 345 return 0; 346} 347 348 349/* Load code.bin file through boot (reset) interface. */ 350static int cycx_code_boot(void __iomem *addr, u8 *code, u32 len) 351{ 352 void __iomem *pt_boot_cmd = addr + CMD_OFFSET; 353 u32 i; 354 355 /* boot buffer length */ 356 writew(CFM_LOAD_BUFSZ, pt_boot_cmd + sizeof(u16)); 357 writew(GEN_DEFPAR, pt_boot_cmd); 358 359 if (wait_cyc(addr) < 0) 360 return -1; 361 362 writew(0x0000, pt_boot_cmd + sizeof(u16)); 363 writew(0xc400, pt_boot_cmd + 2 * sizeof(u16)); 364 writew(GEN_SET_SEG, pt_boot_cmd); 365 366 if (wait_cyc(addr) < 0) 367 return -1; 368 369 for (i = 0 ; i < len ; i += CFM_LOAD_BUFSZ) 370 if (buffer_load(addr, code + i, 371 min_t(u32, CFM_LOAD_BUFSZ, (len - i)))) { 372 printk(KERN_ERR "%s: Error !!\n", modname); 373 return -1; 374 } 375 376 return 0; 377} 378 379/* Load adapter from the memory image of the CYCX firmware module. 380 * o verify firmware integrity and compatibility 381 * o start adapter up */ 382static int load_cyc2x(struct cycx_hw *hw, struct cycx_firmware *cfm, u32 len) 383{ 384 int i, j; 385 struct cycx_fw_header *img_hdr; 386 u8 *reset_image, 387 *data_image, 388 *code_image; 389 void __iomem *pt_cycld = hw->dpmbase + 0x400; 390 u16 cksum; 391 392 /* Announce */ 393 printk(KERN_INFO "%s: firmware signature=\"%s\"\n", modname, 394 cfm->signature); 395 396 /* Verify firmware signature */ 397 if (strcmp(cfm->signature, CFM_SIGNATURE)) { 398 printk(KERN_ERR "%s:load_cyc2x: not Cyclom-2X firmware!\n", 399 modname); 400 return -EINVAL; 401 } 402 403 printk(KERN_INFO "%s: firmware version=%u\n", modname, cfm->version); 404 405 /* Verify firmware module format version */ 406 if (cfm->version != CFM_VERSION) { 407 printk(KERN_ERR "%s:%s: firmware format %u rejected! " 408 "Expecting %u.\n", 409 modname, __func__, cfm->version, CFM_VERSION); 410 return -EINVAL; 411 } 412 413 /* Verify firmware module length and checksum */ 414 cksum = checksum((u8*)&cfm->info, sizeof(struct cycx_fw_info) + 415 cfm->info.codesize); 416 if (cksum != cfm->checksum) { 417 printk(KERN_ERR "%s:%s: firmware corrupted!\n", 418 modname, __func__); 419 printk(KERN_ERR " cdsize = 0x%x (expected 0x%lx)\n", 420 len - (int)sizeof(struct cycx_firmware) - 1, 421 cfm->info.codesize); 422 printk(KERN_ERR " chksum = 0x%x (expected 0x%x)\n", 423 cksum, cfm->checksum); 424 return -EINVAL; 425 } 426 427 /* If everything is ok, set reset, data and code pointers */ 428 img_hdr = (struct cycx_fw_header *)&cfm->image; 429#ifdef FIRMWARE_DEBUG 430 printk(KERN_INFO "%s:%s: image sizes\n", __func__, modname); 431 printk(KERN_INFO " reset=%lu\n", img_hdr->reset_size); 432 printk(KERN_INFO " data=%lu\n", img_hdr->data_size); 433 printk(KERN_INFO " code=%lu\n", img_hdr->code_size); 434#endif 435 reset_image = ((u8 *)img_hdr) + sizeof(struct cycx_fw_header); 436 data_image = reset_image + img_hdr->reset_size; 437 code_image = data_image + img_hdr->data_size; 438 439 /*---- Start load ----*/ 440 /* Announce */ 441 printk(KERN_INFO "%s: loading firmware %s (ID=%u)...\n", modname, 442 cfm->descr[0] ? cfm->descr : "unknown firmware", 443 cfm->info.codeid); 444 445 for (i = 0 ; i < 5 ; i++) { 446 /* Reset Cyclom hardware */ 447 if (!reset_cyc2x(hw->dpmbase)) { 448 printk(KERN_ERR "%s: dpm problem or board not found\n", 449 modname); 450 return -EINVAL; 451 } 452 453 /* Load reset.bin */ 454 cycx_reset_boot(hw->dpmbase, reset_image, img_hdr->reset_size); 455 /* reset is waiting for boot */ 456 writew(GEN_POWER_ON, pt_cycld); 457 msleep_interruptible(1 * 1000); 458 459 for (j = 0 ; j < 3 ; j++) 460 if (!readw(pt_cycld)) 461 goto reset_loaded; 462 else 463 msleep_interruptible(1 * 1000); 464 } 465 466 printk(KERN_ERR "%s: reset not started.\n", modname); 467 return -EINVAL; 468 469reset_loaded: 470 /* Load data.bin */ 471 if (cycx_data_boot(hw->dpmbase, data_image, img_hdr->data_size)) { 472 printk(KERN_ERR "%s: cannot load data file.\n", modname); 473 return -EINVAL; 474 } 475 476 /* Load code.bin */ 477 if (cycx_code_boot(hw->dpmbase, code_image, img_hdr->code_size)) { 478 printk(KERN_ERR "%s: cannot load code file.\n", modname); 479 return -EINVAL; 480 } 481 482 /* Prepare boot-time configuration data */ 483 cycx_bootcfg(hw); 484 485 /* kick-off CPU */ 486 cycx_start(hw->dpmbase); 487 488 /* Arthur Ganzert's tip: wait a while after the firmware loading... 489 seg abr 26 17:17:12 EST 1999 - acme */ 490 msleep_interruptible(7 * 1000); 491 printk(KERN_INFO "%s: firmware loaded!\n", modname); 492 493 /* enable interrupts */ 494 cycx_inten(hw); 495 496 return 0; 497} 498 499/* Prepare boot-time firmware configuration data. 500 * o initialize configuration data area 501 From async.doc - V_3.4.0 - 07/18/1994 502 - As of now, only static buffers are available to the user. 503 So, the bit VD_RXDIRC must be set in 'valid'. That means that user 504 wants to use the static transmission and reception buffers. */ 505static void cycx_bootcfg(struct cycx_hw *hw) 506{ 507 /* use fixed buffers */ 508 writeb(FIXED_BUFFERS, hw->dpmbase + CONF_OFFSET); 509} 510 511/* Detect Cyclom 2x adapter. 512 * Following tests are used to detect Cyclom 2x adapter: 513 * to be completed based on the tests done below 514 * Return 1 if detected o.k. or 0 if failed. 515 * Note: This test is destructive! Adapter will be left in shutdown 516 * state after the test. */ 517static int detect_cyc2x(void __iomem *addr) 518{ 519 reset_cyc2x(addr); 520 521 return memory_exists(addr); 522} 523 524/* Miscellaneous */ 525/* Get option's index into the options list. 526 * Return option's index (1 .. N) or zero if option is invalid. */ 527static int get_option_index(long *optlist, long optval) 528{ 529 int i = 1; 530 531 for (; i <= optlist[0]; ++i) 532 if (optlist[i] == optval) 533 return i; 534 535 return 0; 536} 537 538/* Reset adapter's CPU. */ 539static int reset_cyc2x(void __iomem *addr) 540{ 541 writeb(0, addr + RST_ENABLE); 542 msleep_interruptible(2 * 1000); 543 writeb(0, addr + RST_DISABLE); 544 msleep_interruptible(2 * 1000); 545 546 return memory_exists(addr); 547} 548 549/* Calculate 16-bit CRC using CCITT polynomial. */ 550static u16 checksum(u8 *buf, u32 len) 551{ 552 u16 crc = 0; 553 u16 mask, flag; 554 555 for (; len; --len, ++buf) 556 for (mask = 0x80; mask; mask >>= 1) { 557 flag = (crc & 0x8000); 558 crc <<= 1; 559 crc |= ((*buf & mask) ? 1 : 0); 560 561 if (flag) 562 crc ^= 0x1021; 563 } 564 565 return crc; 566} 567 568module_init(cycx_drv_init); 569module_exit(cycx_drv_cleanup); 570 571/* End */ 572