root/drivers/staging/isdn/hysdn/boardergo.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. ergo_interrupt
  2. ergo_irq_bh
  3. ergo_stopcard
  4. ergo_set_errlog_state
  5. ergo_testram
  6. ergo_writebootimg
  7. ergo_writebootseq
  8. ergo_waitpofready
  9. ergo_releasehardware
  10. ergo_inithardware

   1 /* $Id: boardergo.c,v 1.5.6.7 2001/11/06 21:58:19 kai 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 /***************************************************/
  34 static irqreturn_t
  35 ergo_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 /******************************************************************************/
  73 static void
  74 ergo_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 /*********************************************************/
 127 static void
 128 ergo_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 /**************************************************************************/
 152 static void
 153 ergo_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 /******************************************/
 180 static const char TestText[36] = "This Message is filler, why read it";
 181 
 182 static int
 183 ergo_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 /*****************************************************************************/
 213 static int
 214 ergo_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 /********************************************************************************/
 260 static int
 261 ergo_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 /***********************************************************************************/
 321 static int
 322 ergo_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 /************************************************************************************/
 395 static void
 396 ergo_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 /*********************************************************************************/
 412 int
 413 ergo_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 */

/* [<][>][^][v][top][bottom][index][help] */