1/* $Id: boardergo.c,v 1.5.6.7 2001/11/06 21:58:19 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 will be called as part of the kernel clearing its shared work */ 68/* queue sometime after a call to schedule_work has been made passing our */ 69/* work_struct. This task is the only one handling data transfer from or to */ 70/* the card after booting. The task may be queued from everywhere */ 71/* (interrupts included). */ 72/******************************************************************************/ 73static void 74ergo_irq_bh(struct work_struct *ugli_api) 75{ 76 hysdn_card * card = container_of(ugli_api, hysdn_card, irq_queue); 77 tErgDpram *dpr; 78 int again; 79 unsigned long flags; 80 81 if (card->state != CARD_STATE_RUN) 82 return; /* invalid call */ 83 84 dpr = card->dpram; /* point to DPRAM */ 85 86 spin_lock_irqsave(&card->hysdn_lock, flags); 87 if (card->hw_lock) { 88 spin_unlock_irqrestore(&card->hysdn_lock, flags); /* hardware currently unavailable */ 89 return; 90 } 91 card->hw_lock = 1; /* we now lock the hardware */ 92 93 do { 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 if (again) { 114 dpr->ToHyInt = 1; 115 dpr->ToPcInt = 1; /* interrupt to E1 for all cards */ 116 } else 117 card->hw_lock = 0; /* free hardware again */ 118 } while (again); /* until nothing more to do */ 119 120 spin_unlock_irqrestore(&card->hysdn_lock, flags); 121} /* ergo_irq_bh */ 122 123 124/*********************************************************/ 125/* stop the card (hardware reset) and disable interrupts */ 126/*********************************************************/ 127static void 128ergo_stopcard(hysdn_card * card) 129{ 130 unsigned long flags; 131 unsigned char val; 132 133 hysdn_net_release(card); /* first release the net device if existing */ 134#ifdef CONFIG_HYSDN_CAPI 135 hycapi_capi_stop(card); 136#endif /* CONFIG_HYSDN_CAPI */ 137 spin_lock_irqsave(&card->hysdn_lock, flags); 138 val = bytein(card->iobase + PCI9050_INTR_REG); /* get actual value */ 139 val &= ~(PCI9050_INTR_REG_ENPCI | PCI9050_INTR_REG_EN1); /* mask irq */ 140 byteout(card->iobase + PCI9050_INTR_REG, val); 141 card->irq_enabled = 0; 142 byteout(card->iobase + PCI9050_USER_IO, PCI9050_E1_RESET); /* reset E1 processor */ 143 card->state = CARD_STATE_UNUSED; 144 card->err_log_state = ERRLOG_STATE_OFF; /* currently no log active */ 145 146 spin_unlock_irqrestore(&card->hysdn_lock, flags); 147} /* ergo_stopcard */ 148 149/**************************************************************************/ 150/* enable or disable the cards error log. The event is queued if possible */ 151/**************************************************************************/ 152static void 153ergo_set_errlog_state(hysdn_card * card, int on) 154{ 155 unsigned long flags; 156 157 if (card->state != CARD_STATE_RUN) { 158 card->err_log_state = ERRLOG_STATE_OFF; /* must be off */ 159 return; 160 } 161 spin_lock_irqsave(&card->hysdn_lock, flags); 162 163 if (((card->err_log_state == ERRLOG_STATE_OFF) && !on) || 164 ((card->err_log_state == ERRLOG_STATE_ON) && on)) { 165 spin_unlock_irqrestore(&card->hysdn_lock, flags); 166 return; /* nothing to do */ 167 } 168 if (on) 169 card->err_log_state = ERRLOG_STATE_START; /* request start */ 170 else 171 card->err_log_state = ERRLOG_STATE_STOP; /* request stop */ 172 173 spin_unlock_irqrestore(&card->hysdn_lock, flags); 174 schedule_work(&card->irq_queue); 175} /* ergo_set_errlog_state */ 176 177/******************************************/ 178/* test the cards RAM and return 0 if ok. */ 179/******************************************/ 180static const char TestText[36] = "This Message is filler, why read it"; 181 182static int 183ergo_testram(hysdn_card * card) 184{ 185 tErgDpram *dpr = card->dpram; 186 187 memset(dpr->TrapTable, 0, sizeof(dpr->TrapTable)); /* clear all Traps */ 188 dpr->ToHyInt = 1; /* E1 INTR state forced */ 189 190 memcpy(&dpr->ToHyBuf[ERG_TO_HY_BUF_SIZE - sizeof(TestText)], TestText, 191 sizeof(TestText)); 192 if (memcmp(&dpr->ToHyBuf[ERG_TO_HY_BUF_SIZE - sizeof(TestText)], TestText, 193 sizeof(TestText))) 194 return (-1); 195 196 memcpy(&dpr->ToPcBuf[ERG_TO_PC_BUF_SIZE - sizeof(TestText)], TestText, 197 sizeof(TestText)); 198 if (memcmp(&dpr->ToPcBuf[ERG_TO_PC_BUF_SIZE - sizeof(TestText)], TestText, 199 sizeof(TestText))) 200 return (-1); 201 202 return (0); 203} /* ergo_testram */ 204 205/*****************************************************************************/ 206/* this function is intended to write stage 1 boot image to the cards buffer */ 207/* this is done in two steps. First the 1024 hi-words are written (offs=0), */ 208/* then the 1024 lo-bytes are written. The remaining DPRAM is cleared, the */ 209/* PCI-write-buffers flushed and the card is taken out of reset. */ 210/* The function then waits for a reaction of the E1 processor or a timeout. */ 211/* Negative return values are interpreted as errors. */ 212/*****************************************************************************/ 213static int 214ergo_writebootimg(struct HYSDN_CARD *card, unsigned char *buf, 215 unsigned long offs) 216{ 217 unsigned char *dst; 218 tErgDpram *dpram; 219 int cnt = (BOOT_IMG_SIZE >> 2); /* number of words to move and swap (byte order!) */ 220 221 if (card->debug_flags & LOG_POF_CARD) 222 hysdn_addlog(card, "ERGO: write bootldr offs=0x%lx ", offs); 223 224 dst = card->dpram; /* pointer to start of DPRAM */ 225 dst += (offs + ERG_DPRAM_FILL_SIZE); /* offset in the DPRAM */ 226 while (cnt--) { 227 *dst++ = *(buf + 1); /* high byte */ 228 *dst++ = *buf; /* low byte */ 229 dst += 2; /* point to next longword */ 230 buf += 2; /* buffer only filled with words */ 231 } 232 233 /* if low words (offs = 2) have been written, clear the rest of the DPRAM, */ 234 /* flush the PCI-write-buffer and take the E1 out of reset */ 235 if (offs) { 236 memset(card->dpram, 0, ERG_DPRAM_FILL_SIZE); /* fill the DPRAM still not cleared */ 237 dpram = card->dpram; /* get pointer to dpram structure */ 238 dpram->ToHyNoDpramErrLog = 0xFF; /* write a dpram register */ 239 while (!dpram->ToHyNoDpramErrLog); /* reread volatile register to flush PCI */ 240 241 byteout(card->iobase + PCI9050_USER_IO, PCI9050_E1_RUN); /* start E1 processor */ 242 /* the interrupts are still masked */ 243 244 msleep_interruptible(20); /* Timeout 20ms */ 245 246 if (((tDpramBootSpooler *) card->dpram)->Len != DPRAM_SPOOLER_DATA_SIZE) { 247 if (card->debug_flags & LOG_POF_CARD) 248 hysdn_addlog(card, "ERGO: write bootldr no answer"); 249 return (-ERR_BOOTIMG_FAIL); 250 } 251 } /* start_boot_img */ 252 return (0); /* successful */ 253} /* ergo_writebootimg */ 254 255/********************************************************************************/ 256/* ergo_writebootseq writes the buffer containing len bytes to the E1 processor */ 257/* using the boot spool mechanism. If everything works fine 0 is returned. In */ 258/* case of errors a negative error value is returned. */ 259/********************************************************************************/ 260static int 261ergo_writebootseq(struct HYSDN_CARD *card, unsigned char *buf, int len) 262{ 263 tDpramBootSpooler *sp = (tDpramBootSpooler *) card->dpram; 264 unsigned char *dst; 265 unsigned char buflen; 266 int nr_write; 267 unsigned char tmp_rdptr; 268 unsigned char wr_mirror; 269 int i; 270 271 if (card->debug_flags & LOG_POF_CARD) 272 hysdn_addlog(card, "ERGO: write boot seq len=%d ", len); 273 274 dst = sp->Data; /* point to data in spool structure */ 275 buflen = sp->Len; /* maximum len of spooled data */ 276 wr_mirror = sp->WrPtr; /* only once read */ 277 278 /* try until all bytes written or error */ 279 i = 0x1000; /* timeout value */ 280 while (len) { 281 282 /* first determine the number of bytes that may be buffered */ 283 do { 284 tmp_rdptr = sp->RdPtr; /* first read the pointer */ 285 i--; /* decrement timeout */ 286 } while (i && (tmp_rdptr != sp->RdPtr)); /* wait for stable pointer */ 287 288 if (!i) { 289 if (card->debug_flags & LOG_POF_CARD) 290 hysdn_addlog(card, "ERGO: write boot seq timeout"); 291 return (-ERR_BOOTSEQ_FAIL); /* value not stable -> timeout */ 292 } 293 if ((nr_write = tmp_rdptr - wr_mirror - 1) < 0) 294 nr_write += buflen; /* now we got number of free bytes - 1 in buffer */ 295 296 if (!nr_write) 297 continue; /* no free bytes in buffer */ 298 299 if (nr_write > len) 300 nr_write = len; /* limit if last few bytes */ 301 i = 0x1000; /* reset timeout value */ 302 303 /* now we know how much bytes we may put in the puffer */ 304 len -= nr_write; /* we savely could adjust len before output */ 305 while (nr_write--) { 306 *(dst + wr_mirror) = *buf++; /* output one byte */ 307 if (++wr_mirror >= buflen) 308 wr_mirror = 0; 309 sp->WrPtr = wr_mirror; /* announce the next byte to E1 */ 310 } /* while (nr_write) */ 311 312 } /* while (len) */ 313 return (0); 314} /* ergo_writebootseq */ 315 316/***********************************************************************************/ 317/* ergo_waitpofready waits for a maximum of 10 seconds for the completition of the */ 318/* boot process. If the process has been successful 0 is returned otherwise a */ 319/* negative error code is returned. */ 320/***********************************************************************************/ 321static int 322ergo_waitpofready(struct HYSDN_CARD *card) 323{ 324 tErgDpram *dpr = card->dpram; /* pointer to DPRAM structure */ 325 int timecnt = 10000 / 50; /* timeout is 10 secs max. */ 326 unsigned long flags; 327 int msg_size; 328 int i; 329 330 if (card->debug_flags & LOG_POF_CARD) 331 hysdn_addlog(card, "ERGO: waiting for pof ready"); 332 while (timecnt--) { 333 /* wait until timeout */ 334 335 if (dpr->ToPcFlag) { 336 /* data has arrived */ 337 338 if ((dpr->ToPcChannel != CHAN_SYSTEM) || 339 (dpr->ToPcSize < MIN_RDY_MSG_SIZE) || 340 (dpr->ToPcSize > MAX_RDY_MSG_SIZE) || 341 ((*(unsigned long *) dpr->ToPcBuf) != RDY_MAGIC)) 342 break; /* an error occurred */ 343 344 /* Check for additional data delivered during SysReady */ 345 msg_size = dpr->ToPcSize - RDY_MAGIC_SIZE; 346 if (msg_size > 0) 347 if (EvalSysrTokData(card, dpr->ToPcBuf + RDY_MAGIC_SIZE, msg_size)) 348 break; 349 350 if (card->debug_flags & LOG_POF_RECORD) 351 hysdn_addlog(card, "ERGO: pof boot success"); 352 spin_lock_irqsave(&card->hysdn_lock, flags); 353 354 card->state = CARD_STATE_RUN; /* now card is running */ 355 /* enable the cards interrupt */ 356 byteout(card->iobase + PCI9050_INTR_REG, 357 bytein(card->iobase + PCI9050_INTR_REG) | 358 (PCI9050_INTR_REG_ENPCI | PCI9050_INTR_REG_EN1)); 359 card->irq_enabled = 1; /* we are ready to receive interrupts */ 360 361 dpr->ToPcFlag = 0; /* reset data indicator */ 362 dpr->ToHyInt = 1; 363 dpr->ToPcInt = 1; /* interrupt to E1 for all cards */ 364 365 spin_unlock_irqrestore(&card->hysdn_lock, flags); 366 if ((hynet_enable & (1 << card->myid)) 367 && (i = hysdn_net_create(card))) 368 { 369 ergo_stopcard(card); 370 card->state = CARD_STATE_BOOTERR; 371 return (i); 372 } 373#ifdef CONFIG_HYSDN_CAPI 374 if((i = hycapi_capi_create(card))) { 375 printk(KERN_WARNING "HYSDN: failed to create capi-interface.\n"); 376 } 377#endif /* CONFIG_HYSDN_CAPI */ 378 return (0); /* success */ 379 } /* data has arrived */ 380 msleep_interruptible(50); /* Timeout 50ms */ 381 } /* wait until timeout */ 382 383 if (card->debug_flags & LOG_POF_CARD) 384 hysdn_addlog(card, "ERGO: pof boot ready timeout"); 385 return (-ERR_POF_TIMEOUT); 386} /* ergo_waitpofready */ 387 388 389 390/************************************************************************************/ 391/* release the cards hardware. Before releasing do a interrupt disable and hardware */ 392/* reset. Also unmap dpram. */ 393/* Use only during module release. */ 394/************************************************************************************/ 395static void 396ergo_releasehardware(hysdn_card * card) 397{ 398 ergo_stopcard(card); /* first stop the card if not already done */ 399 free_irq(card->irq, card); /* release interrupt */ 400 release_region(card->iobase + PCI9050_INTR_REG, 1); /* release all io ports */ 401 release_region(card->iobase + PCI9050_USER_IO, 1); 402 iounmap(card->dpram); 403 card->dpram = NULL; /* release shared mem */ 404} /* ergo_releasehardware */ 405 406 407/*********************************************************************************/ 408/* acquire the needed hardware ports and map dpram. If an error occurs a nonzero */ 409/* value is returned. */ 410/* Use only during module init. */ 411/*********************************************************************************/ 412int 413ergo_inithardware(hysdn_card * card) 414{ 415 if (!request_region(card->iobase + PCI9050_INTR_REG, 1, "HYSDN")) 416 return (-1); 417 if (!request_region(card->iobase + PCI9050_USER_IO, 1, "HYSDN")) { 418 release_region(card->iobase + PCI9050_INTR_REG, 1); 419 return (-1); /* ports already in use */ 420 } 421 card->memend = card->membase + ERG_DPRAM_PAGE_SIZE - 1; 422 if (!(card->dpram = ioremap(card->membase, ERG_DPRAM_PAGE_SIZE))) { 423 release_region(card->iobase + PCI9050_INTR_REG, 1); 424 release_region(card->iobase + PCI9050_USER_IO, 1); 425 return (-1); 426 } 427 428 ergo_stopcard(card); /* disable interrupts */ 429 if (request_irq(card->irq, ergo_interrupt, IRQF_SHARED, "HYSDN", card)) { 430 ergo_releasehardware(card); /* return the acquired hardware */ 431 return (-1); 432 } 433 /* success, now setup the function pointers */ 434 card->stopcard = ergo_stopcard; 435 card->releasehardware = ergo_releasehardware; 436 card->testram = ergo_testram; 437 card->writebootimg = ergo_writebootimg; 438 card->writebootseq = ergo_writebootseq; 439 card->waitpofready = ergo_waitpofready; 440 card->set_errlog_state = ergo_set_errlog_state; 441 INIT_WORK(&card->irq_queue, ergo_irq_bh); 442 spin_lock_init(&card->hysdn_lock); 443 444 return (0); 445} /* ergo_inithardware */ 446