Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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