1/* $Id: boardergo.c,v 1.1.1.1 2007/08/03 18:52:36 Exp $ 2 * 3 * Linux driver for HYSDN cards, specific routines for ergo type boards. 4 * 5 * Author Werner Cornelius (werner@titro.de) for Hypercope GmbH 6 * Copyright 1999 by Werner Cornelius (werner@titro.de) 7 * 8 * This software may be used and distributed according to the terms 9 * of the GNU General Public License, incorporated herein by reference. 10 * 11 * As all Linux supported cards Champ2, Ergo and Metro2/4 use the same 12 * DPRAM interface and layout with only minor differences all related 13 * stuff is done here, not in separate modules. 14 * 15 */ 16 17#include <linux/signal.h> 18#include <linux/kernel.h> 19#include <linux/ioport.h> 20#include <linux/interrupt.h> 21#include <linux/vmalloc.h> 22#include <linux/delay.h> 23#include <asm/io.h> 24 25#include "hysdn_defs.h" 26#include "boardergo.h" 27 28#define byteout(addr,val) outb(val,addr) 29#define bytein(addr) inb(addr) 30 31/***************************************************/ 32/* The cards interrupt handler. Called from system */ 33/***************************************************/ 34static irqreturn_t 35ergo_interrupt(int intno, void *dev_id) 36{ 37 hysdn_card *card = dev_id; /* parameter from irq */ 38 tErgDpram *dpr; 39 unsigned long flags; 40 unsigned char volatile b; 41 42 if (!card) 43 return IRQ_NONE; /* error -> spurious interrupt */ 44 if (!card->irq_enabled) 45 return IRQ_NONE; /* other device interrupting or irq switched off */ 46 47 spin_lock_irqsave(&card->hysdn_lock, flags); /* no further irqs allowed */ 48 49 if (!(bytein(card->iobase + PCI9050_INTR_REG) & PCI9050_INTR_REG_STAT1)) { 50 spin_unlock_irqrestore(&card->hysdn_lock, flags); /* restore old state */ 51 return IRQ_NONE; /* no interrupt requested by E1 */ 52 } 53 /* clear any pending ints on the board */ 54 dpr = card->dpram; 55 b = dpr->ToPcInt; /* clear for ergo */ 56 b |= dpr->ToPcIntMetro; /* same for metro */ 57 b |= dpr->ToHyInt; /* and for champ */ 58 59 /* start kernel task immediately after leaving all interrupts */ 60 if (!card->hw_lock) 61 schedule_work(&card->irq_queue); 62 spin_unlock_irqrestore(&card->hysdn_lock, flags); 63 return IRQ_HANDLED; 64} /* ergo_interrupt */ 65 66/******************************************************************************/ 67/* ergo_irq_bh is the function called by the immediate kernel task list after */ 68/* being activated with queue_task and no interrupts active. This task is the */ 69/* only one handling data transfer from or to the card after booting. The task */ 70/* may be queued from everywhere (interrupts included). */ 71/******************************************************************************/ 72static void 73ergo_irq_bh(struct work_struct *ugli_api) 74{ 75 hysdn_card * card = container_of(ugli_api, hysdn_card, irq_queue); 76 tErgDpram *dpr; 77 int again; 78 unsigned long flags; 79 80 if (card->state != CARD_STATE_RUN) 81 return; /* invalid call */ 82 83 dpr = card->dpram; /* point to DPRAM */ 84 85 spin_lock_irqsave(&card->hysdn_lock, flags); 86 if (card->hw_lock) { 87 spin_unlock_irqrestore(&card->hysdn_lock, flags); /* hardware currently unavailable */ 88 return; 89 } 90 card->hw_lock = 1; /* we now lock the hardware */ 91 92 do { 93 sti(); /* reenable other ints */ 94 again = 0; /* assume loop not to be repeated */ 95 96 if (!dpr->ToHyFlag) { 97 /* we are able to send a buffer */ 98 99 if (hysdn_sched_tx(card, dpr->ToHyBuf, &dpr->ToHySize, &dpr->ToHyChannel, 100 ERG_TO_HY_BUF_SIZE)) { 101 dpr->ToHyFlag = 1; /* enable tx */ 102 again = 1; /* restart loop */ 103 } 104 } /* we are able to send a buffer */ 105 if (dpr->ToPcFlag) { 106 /* a message has arrived for us, handle it */ 107 108 if (hysdn_sched_rx(card, dpr->ToPcBuf, dpr->ToPcSize, dpr->ToPcChannel)) { 109 dpr->ToPcFlag = 0; /* we worked the data */ 110 again = 1; /* restart loop */ 111 } 112 } /* a message has arrived for us */ 113 cli(); /* no further ints */ 114 if (again) { 115 dpr->ToHyInt = 1; 116 dpr->ToPcInt = 1; /* interrupt to E1 for all cards */ 117 } else 118 card->hw_lock = 0; /* free hardware again */ 119 } while (again); /* until nothing more to do */ 120 121 spin_unlock_irqrestore(&card->hysdn_lock, flags); 122} /* ergo_irq_bh */ 123 124 125/*********************************************************/ 126/* stop the card (hardware reset) and disable interrupts */ 127/*********************************************************/ 128static void 129ergo_stopcard(hysdn_card * card) 130{ 131 unsigned long flags; 132 unsigned char val; 133 134 hysdn_net_release(card); /* first release the net device if existing */ 135#ifdef CONFIG_HYSDN_CAPI 136 hycapi_capi_stop(card); 137#endif /* CONFIG_HYSDN_CAPI */ 138 spin_lock_irqsave(&card->hysdn_lock, flags); 139 val = bytein(card->iobase + PCI9050_INTR_REG); /* get actual value */ 140 val &= ~(PCI9050_INTR_REG_ENPCI | PCI9050_INTR_REG_EN1); /* mask irq */ 141 byteout(card->iobase + PCI9050_INTR_REG, val); 142 card->irq_enabled = 0; 143 byteout(card->iobase + PCI9050_USER_IO, PCI9050_E1_RESET); /* reset E1 processor */ 144 card->state = CARD_STATE_UNUSED; 145 card->err_log_state = ERRLOG_STATE_OFF; /* currently no log active */ 146 147 spin_unlock_irqrestore(&card->hysdn_lock, flags); 148} /* ergo_stopcard */ 149 150/**************************************************************************/ 151/* enable or disable the cards error log. The event is queued if possible */ 152/**************************************************************************/ 153static void 154ergo_set_errlog_state(hysdn_card * card, int on) 155{ 156 unsigned long flags; 157 158 if (card->state != CARD_STATE_RUN) { 159 card->err_log_state = ERRLOG_STATE_OFF; /* must be off */ 160 return; 161 } 162 spin_lock_irqsave(&card->hysdn_lock, flags); 163 164 if (((card->err_log_state == ERRLOG_STATE_OFF) && !on) || 165 ((card->err_log_state == ERRLOG_STATE_ON) && on)) { 166 spin_unlock_irqrestore(&card->hysdn_lock, flags); 167 return; /* nothing to do */ 168 } 169 if (on) 170 card->err_log_state = ERRLOG_STATE_START; /* request start */ 171 else 172 card->err_log_state = ERRLOG_STATE_STOP; /* request stop */ 173 174 spin_unlock_irqrestore(&card->hysdn_lock, flags); 175 schedule_work(&card->irq_queue); 176} /* ergo_set_errlog_state */ 177 178/******************************************/ 179/* test the cards RAM and return 0 if ok. */ 180/******************************************/ 181static const char TestText[36] = "This Message is filler, why read it"; 182 183static int 184ergo_testram(hysdn_card * card) 185{ 186 tErgDpram *dpr = card->dpram; 187 188 memset(dpr->TrapTable, 0, sizeof(dpr->TrapTable)); /* clear all Traps */ 189 dpr->ToHyInt = 1; /* E1 INTR state forced */ 190 191 memcpy(&dpr->ToHyBuf[ERG_TO_HY_BUF_SIZE - sizeof(TestText)], TestText, 192 sizeof(TestText)); 193 if (memcmp(&dpr->ToHyBuf[ERG_TO_HY_BUF_SIZE - sizeof(TestText)], TestText, 194 sizeof(TestText))) 195 return (-1); 196 197 memcpy(&dpr->ToPcBuf[ERG_TO_PC_BUF_SIZE - sizeof(TestText)], TestText, 198 sizeof(TestText)); 199 if (memcmp(&dpr->ToPcBuf[ERG_TO_PC_BUF_SIZE - sizeof(TestText)], TestText, 200 sizeof(TestText))) 201 return (-1); 202 203 return (0); 204} /* ergo_testram */ 205 206/*****************************************************************************/ 207/* this function is intended to write stage 1 boot image to the cards buffer */ 208/* this is done in two steps. First the 1024 hi-words are written (offs=0), */ 209/* then the 1024 lo-bytes are written. The remaining DPRAM is cleared, the */ 210/* PCI-write-buffers flushed and the card is taken out of reset. */ 211/* The function then waits for a reaction of the E1 processor or a timeout. */ 212/* Negative return values are interpreted as errors. */ 213/*****************************************************************************/ 214static int 215ergo_writebootimg(struct HYSDN_CARD *card, unsigned char *buf, 216 unsigned long offs) 217{ 218 unsigned char *dst; 219 tErgDpram *dpram; 220 int cnt = (BOOT_IMG_SIZE >> 2); /* number of words to move and swap (byte order!) */ 221 222 if (card->debug_flags & LOG_POF_CARD) 223 hysdn_addlog(card, "ERGO: write bootldr offs=0x%lx ", offs); 224 225 dst = card->dpram; /* pointer to start of DPRAM */ 226 dst += (offs + ERG_DPRAM_FILL_SIZE); /* offset in the DPRAM */ 227 while (cnt--) { 228 *dst++ = *(buf + 1); /* high byte */ 229 *dst++ = *buf; /* low byte */ 230 dst += 2; /* point to next longword */ 231 buf += 2; /* buffer only filled with words */ 232 } 233 234 /* if low words (offs = 2) have been written, clear the rest of the DPRAM, */ 235 /* flush the PCI-write-buffer and take the E1 out of reset */ 236 if (offs) { 237 memset(card->dpram, 0, ERG_DPRAM_FILL_SIZE); /* fill the DPRAM still not cleared */ 238 dpram = card->dpram; /* get pointer to dpram structure */ 239 dpram->ToHyNoDpramErrLog = 0xFF; /* write a dpram register */ 240 while (!dpram->ToHyNoDpramErrLog); /* reread volatile register to flush PCI */ 241 242 byteout(card->iobase + PCI9050_USER_IO, PCI9050_E1_RUN); /* start E1 processor */ 243 /* the interrupts are still masked */ 244 245 sti(); 246 msleep_interruptible(20); /* Timeout 20ms */ 247 248 if (((tDpramBootSpooler *) card->dpram)->Len != DPRAM_SPOOLER_DATA_SIZE) { 249 if (card->debug_flags & LOG_POF_CARD) 250 hysdn_addlog(card, "ERGO: write bootldr no answer"); 251 return (-ERR_BOOTIMG_FAIL); 252 } 253 } /* start_boot_img */ 254 return (0); /* successful */ 255} /* ergo_writebootimg */ 256 257/********************************************************************************/ 258/* ergo_writebootseq writes the buffer containing len bytes to the E1 processor */ 259/* using the boot spool mechanism. If everything works fine 0 is returned. In */ 260/* case of errors a negative error value is returned. */ 261/********************************************************************************/ 262static int 263ergo_writebootseq(struct HYSDN_CARD *card, unsigned char *buf, int len) 264{ 265 tDpramBootSpooler *sp = (tDpramBootSpooler *) card->dpram; 266 unsigned char *dst; 267 unsigned char buflen; 268 int nr_write; 269 unsigned char tmp_rdptr; 270 unsigned char wr_mirror; 271 int i; 272 273 if (card->debug_flags & LOG_POF_CARD) 274 hysdn_addlog(card, "ERGO: write boot seq len=%d ", len); 275 276 dst = sp->Data; /* point to data in spool structure */ 277 buflen = sp->Len; /* maximum len of spooled data */ 278 wr_mirror = sp->WrPtr; /* only once read */ 279 sti(); 280 281 /* try until all bytes written or error */ 282 i = 0x1000; /* timeout value */ 283 while (len) { 284 285 /* first determine the number of bytes that may be buffered */ 286 do { 287 tmp_rdptr = sp->RdPtr; /* first read the pointer */ 288 i--; /* decrement timeout */ 289 } while (i && (tmp_rdptr != sp->RdPtr)); /* wait for stable pointer */ 290 291 if (!i) { 292 if (card->debug_flags & LOG_POF_CARD) 293 hysdn_addlog(card, "ERGO: write boot seq timeout"); 294 return (-ERR_BOOTSEQ_FAIL); /* value not stable -> timeout */ 295 } 296 if ((nr_write = tmp_rdptr - wr_mirror - 1) < 0) 297 nr_write += buflen; /* now we got number of free bytes - 1 in buffer */ 298 299 if (!nr_write) 300 continue; /* no free bytes in buffer */ 301 302 if (nr_write > len) 303 nr_write = len; /* limit if last few bytes */ 304 i = 0x1000; /* reset timeout value */ 305 306 /* now we know how much bytes we may put in the puffer */ 307 len -= nr_write; /* we savely could adjust len before output */ 308 while (nr_write--) { 309 *(dst + wr_mirror) = *buf++; /* output one byte */ 310 if (++wr_mirror >= buflen) 311 wr_mirror = 0; 312 sp->WrPtr = wr_mirror; /* announce the next byte to E1 */ 313 } /* while (nr_write) */ 314 315 } /* while (len) */ 316 return (0); 317} /* ergo_writebootseq */ 318 319/***********************************************************************************/ 320/* ergo_waitpofready waits for a maximum of 10 seconds for the completition of the */ 321/* boot process. If the process has been successful 0 is returned otherwise a */ 322/* negative error code is returned. */ 323/***********************************************************************************/ 324static int 325ergo_waitpofready(struct HYSDN_CARD *card) 326{ 327 tErgDpram *dpr = card->dpram; /* pointer to DPRAM structure */ 328 int timecnt = 10000 / 50; /* timeout is 10 secs max. */ 329 unsigned long flags; 330 int msg_size; 331 int i; 332 333 if (card->debug_flags & LOG_POF_CARD) 334 hysdn_addlog(card, "ERGO: waiting for pof ready"); 335 while (timecnt--) { 336 /* wait until timeout */ 337 338 if (dpr->ToPcFlag) { 339 /* data has arrived */ 340 341 if ((dpr->ToPcChannel != CHAN_SYSTEM) || 342 (dpr->ToPcSize < MIN_RDY_MSG_SIZE) || 343 (dpr->ToPcSize > MAX_RDY_MSG_SIZE) || 344 ((*(unsigned long *) dpr->ToPcBuf) != RDY_MAGIC)) 345 break; /* an error occurred */ 346 347 /* Check for additional data delivered during SysReady */ 348 msg_size = dpr->ToPcSize - RDY_MAGIC_SIZE; 349 if (msg_size > 0) 350 if (EvalSysrTokData(card, dpr->ToPcBuf + RDY_MAGIC_SIZE, msg_size)) 351 break; 352 353 if (card->debug_flags & LOG_POF_RECORD) 354 hysdn_addlog(card, "ERGO: pof boot success"); 355 spin_lock_irqsave(&card->hysdn_lock, flags); 356 357 card->state = CARD_STATE_RUN; /* now card is running */ 358 /* enable the cards interrupt */ 359 byteout(card->iobase + PCI9050_INTR_REG, 360 bytein(card->iobase + PCI9050_INTR_REG) | 361 (PCI9050_INTR_REG_ENPCI | PCI9050_INTR_REG_EN1)); 362 card->irq_enabled = 1; /* we are ready to receive interrupts */ 363 364 dpr->ToPcFlag = 0; /* reset data indicator */ 365 dpr->ToHyInt = 1; 366 dpr->ToPcInt = 1; /* interrupt to E1 for all cards */ 367 368 spin_unlock_irqrestore(&card->hysdn_lock, flags); 369 if ((hynet_enable & (1 << card->myid)) 370 && (i = hysdn_net_create(card))) 371 { 372 ergo_stopcard(card); 373 card->state = CARD_STATE_BOOTERR; 374 return (i); 375 } 376#ifdef CONFIG_HYSDN_CAPI 377 if((i = hycapi_capi_create(card))) { 378 printk(KERN_WARNING "HYSDN: failed to create capi-interface.\n"); 379 } 380#endif /* CONFIG_HYSDN_CAPI */ 381 return (0); /* success */ 382 } /* data has arrived */ 383 sti(); 384 msleep_interruptible(50); /* Timeout 50ms */ 385 } /* wait until timeout */ 386 387 if (card->debug_flags & LOG_POF_CARD) 388 hysdn_addlog(card, "ERGO: pof boot ready timeout"); 389 return (-ERR_POF_TIMEOUT); 390} /* ergo_waitpofready */ 391 392 393 394/************************************************************************************/ 395/* release the cards hardware. Before releasing do a interrupt disable and hardware */ 396/* reset. Also unmap dpram. */ 397/* Use only during module release. */ 398/************************************************************************************/ 399static void 400ergo_releasehardware(hysdn_card * card) 401{ 402 ergo_stopcard(card); /* first stop the card if not already done */ 403 free_irq(card->irq, card); /* release interrupt */ 404 release_region(card->iobase + PCI9050_INTR_REG, 1); /* release all io ports */ 405 release_region(card->iobase + PCI9050_USER_IO, 1); 406 iounmap(card->dpram); 407 card->dpram = NULL; /* release shared mem */ 408} /* ergo_releasehardware */ 409 410 411/*********************************************************************************/ 412/* acquire the needed hardware ports and map dpram. If an error occurs a nonzero */ 413/* value is returned. */ 414/* Use only during module init. */ 415/*********************************************************************************/ 416int 417ergo_inithardware(hysdn_card * card) 418{ 419 if (!request_region(card->iobase + PCI9050_INTR_REG, 1, "HYSDN")) 420 return (-1); 421 if (!request_region(card->iobase + PCI9050_USER_IO, 1, "HYSDN")) { 422 release_region(card->iobase + PCI9050_INTR_REG, 1); 423 return (-1); /* ports already in use */ 424 } 425 card->memend = card->membase + ERG_DPRAM_PAGE_SIZE - 1; 426 if (!(card->dpram = ioremap(card->membase, ERG_DPRAM_PAGE_SIZE))) { 427 release_region(card->iobase + PCI9050_INTR_REG, 1); 428 release_region(card->iobase + PCI9050_USER_IO, 1); 429 return (-1); 430 } 431 432 ergo_stopcard(card); /* disable interrupts */ 433 if (request_irq(card->irq, ergo_interrupt, IRQF_SHARED, "HYSDN", card)) { 434 ergo_releasehardware(card); /* return the acquired hardware */ 435 return (-1); 436 } 437 /* success, now setup the function pointers */ 438 card->stopcard = ergo_stopcard; 439 card->releasehardware = ergo_releasehardware; 440 card->testram = ergo_testram; 441 card->writebootimg = ergo_writebootimg; 442 card->writebootseq = ergo_writebootseq; 443 card->waitpofready = ergo_waitpofready; 444 card->set_errlog_state = ergo_set_errlog_state; 445 INIT_WORK(&card->irq_queue, ergo_irq_bh); 446 spin_lock_init(&card->hysdn_lock); 447 448 return (0); 449} /* ergo_inithardware */ 450