1617333575cef20f5918f7ee5cf4337fc9c98219
[safe/jmp/linux-2.6] / drivers / staging / cxt1e1 / pmcc4_drv.c
1 /*
2  * $Id: pmcc4_drv.c,v 3.1 2007/08/15 23:32:17 rickd PMCC4_3_1B $
3  */
4
5
6 /*-----------------------------------------------------------------------------
7  * pmcc4_drv.c -
8  *
9  * Copyright (C) 2007  One Stop Systems, Inc.
10  * Copyright (C) 2002-2006  SBE, Inc.
11  *
12  *   This program is free software; you can redistribute it and/or modify
13  *   it under the terms of the GNU General Public License as published by
14  *   the Free Software Foundation; either version 2 of the License, or
15  *   (at your option) any later version.
16  *
17  *   This program is distributed in the hope that it will be useful,
18  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
19  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  *   GNU General Public License for more details.
21  *
22  * For further information, contact via email: support@onestopsystems.com
23  * One Stop Systems, Inc.  Escondido, California  U.S.A.
24  *-----------------------------------------------------------------------------
25  * RCS info:
26  * RCS revision: $Revision: 3.1 $
27  * Last changed on $Date: 2007/08/15 23:32:17 $
28  * Changed by $Author: rickd $
29  *-----------------------------------------------------------------------------
30  * $Log: pmcc4_drv.c,v $
31  * Revision 3.1  2007/08/15 23:32:17  rickd
32  * Use 'if 0' instead of GNU comment delimeter to avoid line wrap induced compiler errors.
33  *
34  * Revision 3.0  2007/08/15 22:19:55  rickd
35  * Correct sizeof() castings and pi->regram to support 64bit compatibility.
36  *
37  * Revision 2.10  2006/04/21 00:56:40  rickd
38  * workqueue files now prefixed with <sbecom> prefix.
39  *
40  * Revision 2.9  2005/11/01 19:22:49  rickd
41  * Add sanity checks against max_port for ioctl functions.
42  *
43  * Revision 2.8  2005/10/27 18:59:25  rickd
44  * Code cleanup.  Default channel config to HDLC_FCS16.
45  *
46  * Revision 2.7  2005/10/18 18:16:30  rickd
47  * Further NCOMM code repairs - (1) interrupt matrix usage inconsistant
48  * for indexing into nciInterrupt[][], code missing double parameters.
49  * (2) check input of ncomm interrupt registration cardID for correct
50  * boundary values.
51  *
52  * Revision 2.6  2005/10/17 23:55:28  rickd
53  * Initial port of NCOMM support patches from original work found
54  * in pmc_c4t1e1 as updated by NCOMM.  Ref: CONFIG_SBE_PMCC4_NCOMM.
55  * Corrected NCOMMs wanpmcC4T1E1_getBaseAddress() to correctly handle
56  * multiple boards.
57  *
58  * Revision 2.5  2005/10/13 23:01:28  rickd
59  * Correct panic for illegal address reference w/in get_brdinfo on
60  * first_if/last_if name acquistion under Linux 2.6
61  *
62  * Revision 2.4  2005/10/13 21:20:19  rickd
63  * Correction of c4_cleanup() wherein next should be acquired before
64  * ci_t structure is free'd.
65  *
66  * Revision 2.3  2005/10/13 19:20:10  rickd
67  * Correct driver removal cleanup code for multiple boards.
68  *
69  * Revision 2.2  2005/10/11 18:34:04  rickd
70  * New routine added to determine number of ports (comets) on board.
71  *
72  * Revision 2.1  2005/10/05 00:48:13  rickd
73  * Add some RX activation trace code.
74  *
75  * Revision 2.0  2005/09/28 00:10:06  rickd
76  * Implement 2.6 workqueue for TX/RX restart.  Correction to
77  * hardware register boundary checks allows expanded access of MUSYCC.
78  * Implement new musycc reg&bits namings.
79  *
80  *-----------------------------------------------------------------------------
81  */
82
83 char        OSSIid_pmcc4_drvc[] =
84 "@(#)pmcc4_drv.c - $Revision: 3.1 $   (c) Copyright 2002-2007 One Stop Systems, Inc.";
85
86 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
87
88 #if defined (__FreeBSD__) || defined (__NetBSD__)
89 #include <sys/param.h>
90 #include <sys/systm.h>
91 #include <sys/errno.h>
92 #else
93 #include <linux/types.h>
94 #include "pmcc4_sysdep.h"
95 #include <linux/errno.h>
96 #include <linux/kernel.h>
97 #include <linux/sched.h>        /* include for timer */
98 #include <linux/timer.h>        /* include for timer */
99 #include <linux/hdlc.h>
100 #include <asm/io.h>
101 #endif
102
103 #include "sbecom_inline_linux.h"
104 #include "libsbew.h"
105 #include "pmcc4_private.h"
106 #include "pmcc4.h"
107 #include "pmcc4_ioctls.h"
108 #include "musycc.h"
109 #include "comet.h"
110 #include "sbe_bid.h"
111
112 #ifdef SBE_INCLUDE_SYMBOLS
113 #define STATIC
114 #else
115 #define STATIC  static
116 #endif
117
118
119 #define KERN_WARN KERN_WARNING
120
121 /* forward references */
122 #if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,41)
123 status_t    c4_wk_chan_init (mpi_t *, mch_t *);
124 void        c4_wq_port_cleanup (mpi_t *);
125 status_t    c4_wq_port_init (mpi_t *);
126
127 #endif
128 int         c4_loop_port (ci_t *, int, u_int8_t);
129 status_t    c4_set_port (ci_t *, int);
130 status_t    musycc_chan_down (ci_t *, int);
131
132 u_int32_t musycc_chan_proto (int);
133 status_t    musycc_dump_ring (ci_t *, unsigned int);
134 status_t __init musycc_init (ci_t *);
135 void        musycc_init_mdt (mpi_t *);
136 void        musycc_serv_req (mpi_t *, u_int32_t);
137 void        musycc_update_timeslots (mpi_t *);
138
139 extern void musycc_update_tx_thp (mch_t *);
140 extern int  log_level;
141 extern int  max_mru;
142 extern int  max_mtu;
143 extern int  max_rxdesc_used, max_rxdesc_default;
144 extern int  max_txdesc_used, max_txdesc_default;
145
146 #if defined (__powerpc__)
147 extern void *memset (void *s, int c, size_t n);
148
149 #endif
150
151 int         drvr_state = SBE_DRVR_INIT;
152 ci_t       *c4_list = 0;
153 ci_t       *CI;                 /* dummy pointer to board ZEROE's data -
154                                  * DEBUG USAGE */
155
156
157 void
158 sbecom_set_loglevel (int d)
159 {
160     /*
161      * The code within the following -if- clause is a backdoor debug facility
162      * which can be used to display the state of a board's channel.
163      */
164     if (d > LOG_DEBUG)
165     {
166         unsigned int channum = d - (LOG_DEBUG + 1);     /* convert to ZERO
167                                                          * relativity */
168
169         (void) musycc_dump_ring ((ci_t *) CI, channum); /* CI implies support
170                                                          * for card 0 only */
171     } else
172     {
173         if (log_level != d)
174         {
175             printk ("%s: log level changed from %d to %d\n", THIS_MODULE->name, log_level, d);
176             log_level = d;          /* set new */
177         } else
178             printk ("%s: log level is %d\n", THIS_MODULE->name, log_level);
179     }
180 }
181
182
183 mch_t      *
184 c4_find_chan (int channum)
185 {
186     ci_t       *ci;
187     mch_t      *ch;
188     int         portnum, gchan;
189
190     for (ci = c4_list; ci; ci = ci->next)
191         for (portnum = 0; portnum < ci->max_port; portnum++)
192             for (gchan = 0; gchan < MUSYCC_NCHANS; gchan++)
193             {
194                 if ((ch = ci->port[portnum].chan[gchan]))
195                 {
196                     if ((ch->state != UNASSIGNED) &&
197                         (ch->channum == channum))
198                         return (ch);
199                 }
200             }
201     return 0;
202 }
203
204
205 ci_t       *__init
206 c4_new (void *hi)
207 {
208     ci_t       *ci;
209
210 #ifdef SBE_MAP_DEBUG
211     pr_warning("c4_new() entered, ci needs %u.\n",
212                (unsigned int) sizeof (ci_t));
213 #endif
214
215     ci = (ci_t *) OS_kmalloc (sizeof (ci_t));
216     if (ci)
217     {
218         ci->hdw_info = hi;
219         ci->state = C_INIT;         /* mark as hardware not available */
220         ci->next = c4_list;
221         c4_list = ci;
222         ci->brdno = ci->next ? ci->next->brdno + 1 : 0;
223     } else
224         pr_warning("failed CI malloc, size %u.\n",
225                    (unsigned int) sizeof (ci_t));
226
227     if (CI == 0)
228         CI = ci;                    /* DEBUG, only board 0 usage */
229     return ci;
230 }
231
232
233 /***
234  * Check port state and set LED states using watchdog or ioctl...
235  * also check for in-band SF loopback commands (& cause results if they are there)
236  *
237  * Alarm function depends on comet bits indicating change in
238  * link status (linkMask) to keep the link status indication straight.
239  *
240  * Indications are only LED and system log -- except when ioctl is invoked.
241  *
242  * "alarmed" record (a.k.a. copyVal, in some cases below) decodes as:
243  *
244  *   RMAI  (E1 only) 0x100
245  *   alarm LED on    0x80
246  *   link LED on     0x40
247  *   link returned   0x20 (link was down, now it's back and 'port get' hasn't run)
248  *   change in LED   0x10 (update LED register because value has changed)
249  *   link is down    0x08
250  *   YelAlm(RAI)     0x04
251  *   RedAlm          0x02
252  *   AIS(blue)Alm    0x01
253  *
254  * note "link has returned" indication is reset on read
255  * (e.g. by use of the c4_control port get command)
256  */
257
258 #define sbeLinkMask       0x41  /* change in signal status (lost/recovered) +
259                                  * state */
260 #define sbeLinkChange     0x40
261 #define sbeLinkDown       0x01
262 #define sbeAlarmsMask     0x07  /* red / yellow / blue alarm conditions */
263 #define sbeE1AlarmsMask   0x107 /* alarm conditions */
264
265 #define COMET_LBCMD_READ  0x80  /* read only (do not set, return read value) */
266
267 void
268 checkPorts (ci_t * ci)
269 {
270 #ifndef CONFIG_SBE_PMCC4_NCOMM
271     /*
272      * PORT POINT - NCOMM needs to avoid this code since the polling of
273      * alarms conflicts with NCOMM's interrupt servicing implementation.
274      */
275
276     comet_t    *comet;
277     volatile u_int32_t value;
278     u_int32_t   copyVal, LEDval;
279
280     u_int8_t portnum;
281
282     LEDval = 0;
283     for (portnum = 0; portnum < ci->max_port; portnum++)
284     {
285         copyVal = 0x12f & (ci->alarmed[portnum]);       /* port's alarm record */
286         comet = ci->port[portnum].cometbase;
287         value = pci_read_32 ((u_int32_t *) &comet->cdrc_ists) & sbeLinkMask;    /* link loss reg */
288
289         if (value & sbeLinkChange)  /* is there a change in the link stuff */
290         {
291             /* if there's been a change (above) and yet it's the same (below) */
292             if (!(((copyVal >> 3) & sbeLinkDown) ^ (value & sbeLinkDown)))
293             {
294                 if (value & sbeLinkDown)
295                     printk (KERN_WARN "%s: Port %d momentarily recovered.\n",
296                             ci->devname, portnum);
297                 else
298                     printk (KERN_WARN
299                             "%s: Warning: Port %d link was briefly down.\n",
300                             ci->devname, portnum);
301             } else if (value & sbeLinkDown)
302                 printk (KERN_WARN "%s: Warning: Port %d link is down.\n",
303                         ci->devname, portnum);
304             else
305             {
306                 printk (KERN_WARN "%s: Port %d link has recovered.\n",
307                         ci->devname, portnum);
308                 copyVal |= 0x20;    /* record link transition to up */
309             }
310             copyVal |= 0x10;        /* change (link) --> update LEDs  */
311         }
312         copyVal &= 0x137;           /* clear LED & link old history bits &
313                                      * save others */
314         if (value & sbeLinkDown)
315             copyVal |= 0x08;        /* record link status (now) */
316         else
317         {                           /* if link is up, do this */
318             copyVal |= 0x40;        /* LED indicate link is up    */
319             /* Alarm things & the like ... first if E1, then if T1 */
320             if (IS_FRAME_ANY_E1 (ci->port[portnum].p.port_mode))
321             {
322                 /*
323                  * first check Codeword (SaX) changes & CRC and
324                  * sub-multi-frame errors
325                  */
326                 /*
327                  * note these errors are printed every time they are detected
328                  * vs. alarms
329                  */
330                 value = pci_read_32 ((u_int32_t *) &comet->e1_frmr_nat_ists);   /* codeword */
331                 if (value & 0x1f)
332                 {                   /* if errors (crc or smf only) */
333                     if (value & 0x10)
334                         printk (KERN_WARN
335                            "%s: E1 Port %d Codeword Sa4 change detected.\n",
336                                 ci->devname, portnum);
337                     if (value & 0x08)
338                         printk (KERN_WARN
339                            "%s: E1 Port %d Codeword Sa5 change detected.\n",
340                                 ci->devname, portnum);
341                     if (value & 0x04)
342                         printk (KERN_WARN
343                            "%s: E1 Port %d Codeword Sa6 change detected.\n",
344                                 ci->devname, portnum);
345                     if (value & 0x02)
346                         printk (KERN_WARN
347                            "%s: E1 Port %d Codeword Sa7 change detected.\n",
348                                 ci->devname, portnum);
349                     if (value & 0x01)
350                         printk (KERN_WARN
351                            "%s: E1 Port %d Codeword Sa8 change detected.\n",
352                                 ci->devname, portnum);
353                 }
354                 value = pci_read_32 ((u_int32_t *) &comet->e1_frmr_mists);      /* crc & smf */
355                 if (value & 0x3)
356                 {                   /* if errors (crc or smf only) */
357                     if (value & sbeE1CRC)
358                         printk (KERN_WARN "%s: E1 Port %d CRC-4 error(s) detected.\n",
359                                 ci->devname, portnum);
360                     if (value & sbeE1errSMF)    /* error in sub-multiframe */
361                         printk (KERN_WARN "%s: E1 Port %d received errored SMF.\n",
362                                 ci->devname, portnum);
363                 }
364                 value = pci_read_32 ((u_int32_t *) &comet->e1_frmr_masts) & 0xcc; /* alarms */
365                 /*
366                  * pack alarms together (bitmiser), and construct similar to
367                  * T1
368                  */
369                 /* RAI,RMAI,.,.,LOF,AIS,.,. ==>  RMAI,.,.,.,.,.,RAI,LOF,AIS */
370                 /* see 0x97 */
371                 value = (value >> 2);
372                 if (value & 0x30)
373                 {
374                     if (value & 0x20)
375                         value |= 0x40;  /* RAI */
376                     if (value & 0x10)
377                         value |= 0x100; /* RMAI */
378                     value &= ~0x30;
379                 }                   /* finished packing alarm in handy order */
380                 if (value != (copyVal & sbeE1AlarmsMask))
381                 {                   /* if alarms changed */
382                     copyVal |= 0x10;/* change LED status   */
383                     if ((copyVal & sbeRedAlm) && !(value & sbeRedAlm))
384                     {
385                         copyVal &= ~sbeRedAlm;
386                         printk (KERN_WARN "%s: E1 Port %d LOF alarm ended.\n",
387                                 ci->devname, portnum);
388                     } else if (!(copyVal & sbeRedAlm) && (value & sbeRedAlm))
389                     {
390                         copyVal |= sbeRedAlm;
391                         printk (KERN_WARN "%s: E1 Warning: Port %d LOF alarm.\n",
392                                 ci->devname, portnum);
393                     } else if ((copyVal & sbeYelAlm) && !(value & sbeYelAlm))
394                     {
395                         copyVal &= ~sbeYelAlm;
396                         printk (KERN_WARN "%s: E1 Port %d RAI alarm ended.\n",
397                                 ci->devname, portnum);
398                     } else if (!(copyVal & sbeYelAlm) && (value & sbeYelAlm))
399                     {
400                         copyVal |= sbeYelAlm;
401                         printk (KERN_WARN "%s: E1 Warning: Port %d RAI alarm.\n",
402                                 ci->devname, portnum);
403                     } else if ((copyVal & sbeE1RMAI) && !(value & sbeE1RMAI))
404                     {
405                         copyVal &= ~sbeE1RMAI;
406                         printk (KERN_WARN "%s: E1 Port %d RMAI alarm ended.\n",
407                                 ci->devname, portnum);
408                     } else if (!(copyVal & sbeE1RMAI) && (value & sbeE1RMAI))
409                     {
410                         copyVal |= sbeE1RMAI;
411                         printk (KERN_WARN "%s: E1 Warning: Port %d RMAI alarm.\n",
412                                 ci->devname, portnum);
413                     } else if ((copyVal & sbeAISAlm) && !(value & sbeAISAlm))
414                     {
415                         copyVal &= ~sbeAISAlm;
416                         printk (KERN_WARN "%s: E1 Port %d AIS alarm ended.\n",
417                                 ci->devname, portnum);
418                     } else if (!(copyVal & sbeAISAlm) && (value & sbeAISAlm))
419                     {
420                         copyVal |= sbeAISAlm;
421                         printk (KERN_WARN "%s: E1 Warning: Port %d AIS alarm.\n",
422                                 ci->devname, portnum);
423                     }
424                 }
425                 /* end of E1 alarm code */
426             } else
427             {                       /* if a T1 mode */
428                 value = pci_read_32 ((u_int32_t *) &comet->t1_almi_ists);       /* alarms */
429                 value &= sbeAlarmsMask;
430                 if (value != (copyVal & sbeAlarmsMask))
431                 {                   /* if alarms changed */
432                     copyVal |= 0x10;/* change LED status   */
433                     if ((copyVal & sbeRedAlm) && !(value & sbeRedAlm))
434                     {
435                         copyVal &= ~sbeRedAlm;
436                         printk (KERN_WARN "%s: Port %d red alarm ended.\n",
437                                 ci->devname, portnum);
438                     } else if (!(copyVal & sbeRedAlm) && (value & sbeRedAlm))
439                     {
440                         copyVal |= sbeRedAlm;
441                         printk (KERN_WARN "%s: Warning: Port %d red alarm.\n",
442                                 ci->devname, portnum);
443                     } else if ((copyVal & sbeYelAlm) && !(value & sbeYelAlm))
444                     {
445                         copyVal &= ~sbeYelAlm;
446                         printk (KERN_WARN "%s: Port %d yellow (RAI) alarm ended.\n",
447                                 ci->devname, portnum);
448                     } else if (!(copyVal & sbeYelAlm) && (value & sbeYelAlm))
449                     {
450                         copyVal |= sbeYelAlm;
451                         printk (KERN_WARN "%s: Warning: Port %d yellow (RAI) alarm.\n",
452                                 ci->devname, portnum);
453                     } else if ((copyVal & sbeAISAlm) && !(value & sbeAISAlm))
454                     {
455                         copyVal &= ~sbeAISAlm;
456                         printk (KERN_WARN "%s: Port %d blue (AIS) alarm ended.\n",
457                                 ci->devname, portnum);
458                     } else if (!(copyVal & sbeAISAlm) && (value & sbeAISAlm))
459                     {
460                         copyVal |= sbeAISAlm;
461                         printk (KERN_WARN "%s: Warning: Port %d blue (AIS) alarm.\n",
462                                 ci->devname, portnum);
463                     }
464                 }
465             }                       /* end T1 mode alarm checks */
466         }
467         if (copyVal & sbeAlarmsMask)
468             copyVal |= 0x80;        /* if alarm turn yel LED on */
469         if (copyVal & 0x10)
470             LEDval |= 0x100;        /* tag if LED values have changed  */
471         LEDval |= ((copyVal & 0xc0) >> (6 - (portnum * 2)));
472
473         ci->alarmed[portnum] &= 0xfffff000;     /* out with the old (it's fff
474                                                  * ... foo) */
475         ci->alarmed[portnum] |= (copyVal);      /* in with the new */
476
477         /*
478          * enough with the alarms and LED's, now let's check for loopback
479          * requests
480          */
481
482         if (IS_FRAME_ANY_T1 (ci->port[portnum].p.port_mode))
483         {                           /* if a T1 mode  */
484             /*
485              * begin in-band (SF) loopback code detection -- start by reading
486              * command
487              */
488             value = pci_read_32 ((u_int32_t *) &comet->ibcd_ies);       /* detect reg. */
489             value &= 0x3;           /* trim to handy bits */
490             if (value & 0x2)
491             {                       /* activate loopback (sets for deactivate
492                                      * code length) */
493                 copyVal = c4_loop_port (ci, portnum, COMET_LBCMD_READ); /* read line loopback
494                                                                          * mode */
495                 if (copyVal != COMET_MDIAG_LINELB)      /* don't do it again if
496                                                          * already in that mode */
497                     c4_loop_port (ci, portnum, COMET_MDIAG_LINELB);     /* put port in line
498                                                                          * loopback mode */
499             }
500             if (value & 0x1)
501             {                       /* deactivate loopback (sets for activate
502                                      * code length) */
503                 copyVal = c4_loop_port (ci, portnum, COMET_LBCMD_READ); /* read line loopback
504                                                                          * mode */
505                 if (copyVal != COMET_MDIAG_LBOFF)       /* don't do it again if
506                                                          * already in that mode */
507                     c4_loop_port (ci, portnum, COMET_MDIAG_LBOFF);      /* take port out of any
508                                                                          * loopback mode */
509             }
510         }
511         if (IS_FRAME_ANY_T1ESF (ci->port[portnum].p.port_mode))
512         {                           /* if a T1 ESF mode  */
513             /* begin ESF loopback code */
514             value = pci_read_32 ((u_int32_t *) &comet->t1_rboc_sts) & 0x3f;     /* read command */
515             if (value == 0x07)
516                 c4_loop_port (ci, portnum, COMET_MDIAG_LINELB); /* put port in line
517                                                                  * loopback mode */
518             if (value == 0x0a)
519                 c4_loop_port (ci, portnum, COMET_MDIAG_PAYLB);  /* put port in payload
520                                                                  * loopbk mode */
521             if ((value == 0x1c) || (value == 0x19) || (value == 0x12))
522                 c4_loop_port (ci, portnum, COMET_MDIAG_LBOFF);  /* take port out of any
523                                                                  * loopbk mode */
524             if (log_level >= LOG_DEBUG)
525                 if (value != 0x3f)
526                     printk (KERN_WARN "%s: BOC value = %x on Port %d\n",
527                             ci->devname, value, portnum);
528             /* end ESF loopback code */
529         }
530     }
531
532     /* if something is new, update LED's */
533     if (LEDval & 0x100)
534         pci_write_32 ((u_int32_t *) &ci->cpldbase->leds, LEDval & 0xff);
535 #endif                              /*** CONFIG_SBE_PMCC4_NCOMM ***/
536 }
537
538
539 STATIC void
540 c4_watchdog (ci_t * ci)
541 {
542 #if 0
543     //unsigned long flags;
544 #endif
545
546     if (drvr_state != SBE_DRVR_AVAILABLE)
547     {
548         if (log_level >= LOG_MONITOR)
549             printk ("%s: drvr not available (%x)\n", THIS_MODULE->name, drvr_state);
550         return;
551     }
552 #if 0
553     SD_SEM_TAKE (&ci->sem_wdbusy, "_wd_");    /* only 1 thru here, per
554                                                * board */
555 #endif
556
557     ci->wdcount++;
558     checkPorts (ci);
559 #if LINUX_VERSION_CODE <= KERNEL_VERSION(2,5,41)
560     if (ci->wd_notify)
561     {                               /* is there a state change to search for */
562         int         port, gchan;
563
564         ci->wd_notify = 0;          /* reset notification */
565         for (gchan = 0; gchan < MUSYCC_NCHANS; gchan++)
566         {
567             for (port = 0; port < ci->max_port; port++)
568             {
569                 mch_t      *ch = ci->port[port].chan[gchan];
570
571                 if (!ch || ci->state != C_RUNNING)      /* state changed while
572                                                          * acquiring semaphore */
573                     break;
574                 if (ch->state == UP)/* channel must be set up */
575                 {
576 #if 0
577 #ifdef RLD_TRANS_DEBUG
578                     if (1 || log_level >= LOG_MONITOR)
579 #else
580                     if (log_level >= LOG_MONITOR)
581 #endif
582                         printk ("%s: watchdog reviving Port %d Channel %d [%d] sts %x/%x, start_TX %x free %x start_RX %x\n",
583                          ci->devname, ch->channum, port, gchan, ch->channum,
584                                 ch->p.status, ch->status,
585                             ch->ch_start_tx, ch->txd_free, ch->ch_start_rx);
586 #endif
587
588                     /**********************************/
589                     /** check for RX restart request **/
590                     /**********************************/
591
592                     if (ch->ch_start_rx &&
593                         (ch->status & RX_ENABLED))      /* requires start on
594                                                          * enabled RX */
595                     {
596                         ch->ch_start_rx = 0;    /* we are restarting RX... */
597 #ifdef RLD_TRANS_DEBUG
598                         printk ("++ c4_watchdog() CHAN RX ACTIVATE: chan %d\n", ch->channum);
599 #endif
600 #ifdef RLD_RXACT_DEBUG
601                         {
602                             struct mdesc *md;
603                             static int  hereb4 = 7;
604
605                             if (hereb4)
606                             {
607                                 hereb4--;
608                                 md = &ch->mdr[ch->rxix_irq_srv];
609                                 printk ("++ c4_watchdog[%d] CHAN RX ACTIVATE: rxix_irq_srv %d, md %p sts %x, rxpkt %lu\n",
610                                         ch->channum, ch->rxix_irq_srv, md, le32_to_cpu (md->status), ch->s.rx_packets);
611                                 musycc_dump_rxbuffer_ring (ch, 1);      /* RLD DEBUG */
612                             }
613                         }
614 #endif
615                         musycc_serv_req (ch->up, SR_CHANNEL_ACTIVATE | SR_RX_DIRECTION | gchan);
616                     }
617                     /**********************************/
618                     /** check for TX restart request **/
619                     /**********************************/
620
621                     if (ch->ch_start_tx &&
622                         (ch->status & TX_ENABLED))      /* requires start on
623                                                          * enabled TX */
624                     {
625                         struct mdesc *md;
626
627                         /*
628                          * find next unprocessed message, then set TX thp to
629                          * it
630                          */
631                         musycc_update_tx_thp (ch);
632
633 #if 0
634                         spin_lock_irqsave (&ch->ch_txlock, flags);
635 #endif
636                         md = ch->txd_irq_srv;
637                         if (!md)
638                         {
639                             printk ("-- c4_watchdog[%d]: WARNING, starting NULL md\n", ch->channum);
640                             printk ("--   chan %d txd_irq_srv %p sts %x usr_add %p sts %x, txpkt %lu\n",
641                                     ch->channum, ch->txd_irq_srv, le32_to_cpu ((struct mdesc *) (ch->txd_irq_srv)->status),
642                                     ch->txd_usr_add, le32_to_cpu ((struct mdesc *) (ch->txd_usr_add)->status),
643                                     ch->s.tx_packets);
644 #if 0
645                             spin_unlock_irqrestore (&ch->ch_txlock, flags);
646 #endif
647                         } else if (md->data && ((le32_to_cpu (md->status)) & MUSYCC_TX_OWNED))
648                         {
649 #ifdef RLD_TRANS_DEBUG
650                             printk ("++ c4_watchdog[%d] CHAN TX ACTIVATE: start_tx %x\n", ch->channum, ch->ch_start_tx);
651 #endif
652                             ch->ch_start_tx = 0;        /* we are restarting
653                                                          * TX... */
654 #if 0
655                             spin_unlock_irqrestore (&ch->ch_txlock, flags);   /* allow interrupts for
656                                                                                * service request */
657 #endif
658                             musycc_serv_req (ch->up, SR_CHANNEL_ACTIVATE | SR_TX_DIRECTION | gchan);
659 #ifdef RLD_TRANS_DEBUG
660                             if (1 || log_level >= LOG_MONITOR)
661 #else
662                             if (log_level >= LOG_MONITOR)
663 #endif
664                                 printk ("++ SACK[P%d/C%d] ack'd, continuing...\n", ch->up->portnum, ch->channum);
665                         }
666                     }
667                 }
668             }
669         }
670     }
671 #else
672     ci->wd_notify = 0;
673 #endif
674 #if 0
675     SD_SEM_GIVE (&ci->sem_wdbusy);/* release per-board hold */
676 #endif
677 }
678
679
680 void
681 c4_cleanup (void)
682 {
683     ci_t       *ci, *next;
684     mpi_t      *pi;
685     int         portnum, j;
686
687     ci = c4_list;
688     while (ci)
689     {
690         next = ci->next;            /* protect <next> from upcoming <free> */
691         pci_write_32 ((u_int32_t *) &ci->cpldbase->leds, PMCC4_CPLD_LED_OFF);
692         for (portnum = 0; portnum < ci->max_port; portnum++)
693         {
694             pi = &ci->port[portnum];
695 #if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,41)
696             c4_wq_port_cleanup (pi);
697 #endif
698             for (j = 0; j < MUSYCC_NCHANS; j++)
699             {
700                 if (pi->chan[j])
701                     OS_kfree (pi->chan[j]);     /* free mch_t struct */
702             }
703             OS_kfree (pi->regram_saved);
704         }
705 #if 0
706         /* obsolete - watchdog is now static w/in ci_t */
707         OS_free_watchdog (ci->wd);
708 #endif
709         OS_kfree (ci->iqd_p_saved);
710         OS_kfree (ci);
711         ci = next;                  /* cleanup next board, if any */
712     }
713 }
714
715
716 /*
717  * This function issues a write to all comet chips and expects the same data
718  * to be returned from the subsequent read.  This determines the board build
719  * to be a 1-port, 2-port, or 4-port build.  The value returned represents a
720  * bit-mask of the found ports.  Only certain configurations are considered
721  * VALID or LEGAL builds.
722  */
723
724 int
725 c4_get_portcfg (ci_t * ci)
726 {
727     comet_t    *comet;
728     int         portnum, mask;
729     u_int32_t   wdata, rdata;
730
731     wdata = COMET_MDIAG_LBOFF;      /* take port out of any loopback mode */
732
733     mask = 0;
734     for (portnum = 0; portnum < MUSYCC_NPORTS; portnum++)
735     {
736         comet = ci->port[portnum].cometbase;
737         pci_write_32 ((u_int32_t *) &comet->mdiag, wdata);
738         rdata = pci_read_32 ((u_int32_t *) &comet->mdiag) & COMET_MDIAG_LBMASK;
739         if (wdata == rdata)
740             mask |= 1 << portnum;
741     }
742     return mask;
743 }
744
745
746 /* nothing herein should generate interrupts */
747
748 status_t    __init
749 c4_init (ci_t * ci, u_char *func0, u_char *func1)
750 {
751     mpi_t      *pi;
752     mch_t      *ch;
753     static u_int32_t count = 0;
754     int         portnum, j;
755
756     ci->state = C_INIT;
757     ci->brdno = count++;
758     ci->intlog.this_status_new = 0;
759     atomic_set (&ci->bh_pending, 0);
760
761     ci->reg = (struct musycc_globalr *) func0;
762     ci->eeprombase = (u_int32_t *) (func1 + EEPROM_OFFSET);
763     ci->cpldbase = (c4cpld_t *) ((u_int32_t *) (func1 + ISPLD_OFFSET));
764
765     /*** PORT POINT - the following is the first access of any type to the hardware ***/
766 #ifdef CONFIG_SBE_PMCC4_NCOMM
767     /* NCOMM driver uses INTB interrupt to monitor CPLD register */
768     pci_write_32 ((u_int32_t *) &ci->reg->glcd, GCD_MAGIC);
769 #else
770     /* standard driver POLLS for INTB via CPLD register */
771     pci_write_32 ((u_int32_t *) &ci->reg->glcd, GCD_MAGIC | MUSYCC_GCD_INTB_DISABLE);
772 #endif
773
774     {
775         int         pmsk;
776
777         /* need comet addresses available for determination of hardware build */
778         for (portnum = 0; portnum < MUSYCC_NPORTS; portnum++)
779         {
780             pi = &ci->port[portnum];
781             pi->cometbase = (comet_t *) ((u_int32_t *) (func1 + COMET_OFFSET (portnum)));
782             pi->reg = (struct musycc_globalr *) ((u_char *) ci->reg + (portnum * 0x800));
783             pi->portnum = portnum;
784             pi->p.portnum = portnum;
785             pi->openchans = 0;
786 #ifdef SBE_MAP_DEBUG
787             printk ("Comet-%d: addr = %p\n", portnum, pi->cometbase);
788 #endif
789         }
790         pmsk = c4_get_portcfg (ci);
791         switch (pmsk)
792         {
793         case 0x1:
794             ci->max_port = 1;
795             break;
796         case 0x3:
797             ci->max_port = 2;
798             break;
799 #if 0
800         case 0x7:                   /* not built, but could be... */
801             ci->max_port = 3;
802             break;
803 #endif
804         case 0xf:
805             ci->max_port = 4;
806             break;
807         default:
808             ci->max_port = 0;
809             pr_warning("%s: illegal port configuration (%x)\n",
810                        ci->devname, pmsk);
811             return SBE_DRVR_FAIL;
812         }
813 #ifdef SBE_MAP_DEBUG
814         printk (">> %s: c4_get_build - pmsk %x max_port %x\n", ci->devname, pmsk, ci->max_port);
815 #endif
816     }
817
818     for (portnum = 0; portnum < ci->max_port; portnum++)
819     {
820         pi = &ci->port[portnum];
821         pi->up = ci;
822         pi->sr_last = 0xffffffff;
823         pi->p.port_mode = CFG_FRAME_SF; /* T1 B8ZS, the default */
824         pi->p.portP = (CFG_CLK_PORT_EXTERNAL | CFG_LBO_LH0);    /* T1 defaults */
825
826         OS_sem_init (&pi->sr_sem_busy, SEM_AVAILABLE);
827         OS_sem_init (&pi->sr_sem_wait, SEM_TAKEN);
828
829         for (j = 0; j < 32; j++)
830         {
831             pi->fifomap[j] = -1;
832             pi->tsm[j] = 0;         /* no assignments, all available */
833         }
834
835         /* allocate channel structures for this port */
836         for (j = 0; j < MUSYCC_NCHANS; j++)
837         {
838             ch = OS_kmalloc (sizeof (mch_t));
839             if (ch)
840             {
841                 pi->chan[j] = ch;
842                 ch->state = UNASSIGNED;
843                 ch->up = pi;
844                 ch->gchan = (-1);   /* channel assignment not yet known */
845                 ch->channum = (-1); /* channel assignment not yet known */
846                 ch->p.card = ci->brdno;
847                 ch->p.port = portnum;
848                 ch->p.channum = (-1);   /* channel assignment not yet known */
849                 ch->p.mode_56k = 0; /* default is 64kbps mode */
850             } else
851             {
852                 pr_warning("failed mch_t malloc, port %d channel %d size %u.\n",
853                            portnum, j, (unsigned int) sizeof (mch_t));
854                 break;
855             }
856         }
857     }
858
859
860     {
861         /*
862          * Set LEDs through their paces to supply visual proof that LEDs are
863          * functional and not burnt out nor broken.
864          *
865          * YELLOW + GREEN -> OFF.
866          */
867
868         pci_write_32 ((u_int32_t *) &ci->cpldbase->leds,
869                       PMCC4_CPLD_LED_GREEN | PMCC4_CPLD_LED_YELLOW);
870         OS_uwait (750000, "leds");
871         pci_write_32 ((u_int32_t *) &ci->cpldbase->leds, PMCC4_CPLD_LED_OFF);
872     }
873
874     OS_init_watchdog (&ci->wd, (void (*) (void *)) c4_watchdog, ci, WATCHDOG_TIMEOUT);
875     return SBE_DRVR_SUCCESS;
876 }
877
878
879 /* better be fully setup to handle interrupts when you call this */
880
881 status_t    __init
882 c4_init2 (ci_t * ci)
883 {
884     status_t    ret;
885
886     /* PORT POINT: this routine generates first interrupt */
887     if ((ret = musycc_init (ci)) != SBE_DRVR_SUCCESS)
888         return ret;
889
890 #if 0
891     ci->p.framing_type = FRAMING_CBP;
892     ci->p.h110enable = 1;
893 #if 0
894     ci->p.hypersize = 0;
895 #else
896     hyperdummy = 0;
897 #endif
898     ci->p.clock = 0;                /* Use internal clocking until set to
899                                      * external */
900     c4_card_set_params (ci, &ci->p);
901 #endif
902     OS_start_watchdog (&ci->wd);
903     return SBE_DRVR_SUCCESS;
904 }
905
906
907 /* This function sets the loopback mode (or clears it, as the case may be). */
908
909 int
910 c4_loop_port (ci_t * ci, int portnum, u_int8_t cmd)
911 {
912     comet_t    *comet;
913     volatile u_int32_t loopValue;
914
915     comet = ci->port[portnum].cometbase;
916     loopValue = pci_read_32 ((u_int32_t *) &comet->mdiag) & COMET_MDIAG_LBMASK;
917
918     if (cmd & COMET_LBCMD_READ)
919         return loopValue;           /* return the read value */
920
921     if (loopValue != cmd)
922     {
923         switch (cmd)
924         {
925         case COMET_MDIAG_LINELB:
926             /* set(SF)loopback down (turn off) code length to 6 bits */
927             pci_write_32 ((u_int32_t *) &comet->ibcd_cfg, 0x05);
928             break;
929         case COMET_MDIAG_LBOFF:
930             /* set (SF) loopback up (turn on) code length to 5 bits */
931             pci_write_32 ((u_int32_t *) &comet->ibcd_cfg, 0x00);
932             break;
933         }
934
935         pci_write_32 ((u_int32_t *) &comet->mdiag, cmd);
936         if (log_level >= LOG_WARN)
937             printk ("%s: loopback mode changed to %2x from %2x on Port %d\n",
938                     ci->devname, cmd, loopValue, portnum);
939         loopValue = pci_read_32 ((u_int32_t *) &comet->mdiag) & COMET_MDIAG_LBMASK;
940         if (loopValue != cmd)
941         {
942             if (log_level >= LOG_ERROR)
943                 printk ("%s: write to loop register failed, unknown state for Port %d\n",
944                         ci->devname, portnum);
945         }
946     } else
947     {
948         if (log_level >= LOG_WARN)
949             printk ("%s: loopback already in that mode (%2x)\n", ci->devname, loopValue);
950     }
951     return 0;
952 }
953
954
955 /* c4_frame_rw: read or write the comet register specified
956  * (modifies use of port_param to non-standard use of struct)
957  * Specifically:
958  *   pp.portnum     (one guess)
959  *   pp.port_mode   offset of register
960  *   pp.portP       write (or not, i.e. read)
961  *   pp.portStatus  write value
962  * BTW:
963  *   pp.portStatus  also used to return read value
964  *   pp.portP       also used during write, to return old reg value
965  */
966
967 status_t
968 c4_frame_rw (ci_t * ci, struct sbecom_port_param * pp)
969 {
970     comet_t    *comet;
971     volatile u_int32_t data;
972
973     if (pp->portnum >= ci->max_port)/* sanity check */
974         return ENXIO;
975
976     comet = ci->port[pp->portnum].cometbase;
977     data = pci_read_32 ((u_int32_t *) comet + pp->port_mode) & 0xff;
978
979     if (pp->portP)
980     {                               /* control says this is a register
981                                      * _write_ */
982         if (pp->portStatus == data)
983             printk ("%s: Port %d already that value!  Writing again anyhow.\n",
984                     ci->devname, pp->portnum);
985         pp->portP = (u_int8_t) data;
986         pci_write_32 ((u_int32_t *) comet + pp->port_mode,
987                       pp->portStatus);
988         data = pci_read_32 ((u_int32_t *) comet + pp->port_mode) & 0xff;
989     }
990     pp->portStatus = (u_int8_t) data;
991     return 0;
992 }
993
994
995 /* c4_pld_rw: read or write the pld register specified
996  * (modifies use of port_param to non-standard use of struct)
997  * Specifically:
998  *   pp.port_mode   offset of register
999  *   pp.portP       write (or not, i.e. read)
1000  *   pp.portStatus  write value
1001  * BTW:
1002  *   pp.portStatus  also used to return read value
1003  *   pp.portP       also used during write, to return old reg value
1004  */
1005
1006 status_t
1007 c4_pld_rw (ci_t * ci, struct sbecom_port_param * pp)
1008 {
1009     volatile u_int32_t *regaddr;
1010     volatile u_int32_t data;
1011     int         regnum = pp->port_mode;
1012
1013     regaddr = (u_int32_t *) ci->cpldbase + regnum;
1014     data = pci_read_32 ((u_int32_t *) regaddr) & 0xff;
1015
1016     if (pp->portP)
1017     {                               /* control says this is a register
1018                                      * _write_ */
1019         pp->portP = (u_int8_t) data;
1020         pci_write_32 ((u_int32_t *) regaddr, pp->portStatus);
1021         data = pci_read_32 ((u_int32_t *) regaddr) & 0xff;
1022     }
1023     pp->portStatus = (u_int8_t) data;
1024     return 0;
1025 }
1026
1027 /* c4_musycc_rw: read or write the musycc register specified
1028  * (modifies use of port_param to non-standard use of struct)
1029  * Specifically:
1030  *    mcp.RWportnum   port number and write indication bit (0x80)
1031  *    mcp.offset      offset of register
1032  *    mcp.value       write value going in and read value returning
1033  */
1034
1035 /* PORT POINT: TX Subchannel Map registers are write-only
1036  * areas within the MUSYCC and always return FF */
1037 /* PORT POINT: regram and reg structures are minorly different and <offset> ioctl
1038  * settings are aligned with the <reg> struct musycc_globalr{} usage.
1039  * Also, regram is separately allocated shared memory, allocated for each port.
1040  * PORT POINT: access offsets of 0x6000 for Msg Cfg Desc Tbl are for 4-port MUSYCC
1041  * only.  (An 8-port MUSYCC has 0x16000 offsets for accessing its upper 4 tables.)
1042  */
1043
1044 status_t
1045 c4_musycc_rw (ci_t * ci, struct c4_musycc_param * mcp)
1046 {
1047     mpi_t      *pi;
1048     volatile u_int32_t *dph;    /* hardware implemented register */
1049     u_int32_t  *dpr = 0;        /* RAM image of registers for group command
1050                                  * usage */
1051     int         offset = mcp->offset % 0x800;   /* group relative address
1052                                                  * offset, mcp->portnum is
1053                                                  * not used */
1054     int         portnum, ramread = 0;
1055     volatile u_int32_t data;
1056
1057     /*
1058      * Sanity check hardware accessibility.  The 0x6000 portion handles port
1059      * numbers associated with Msg Descr Tbl decoding.
1060      */
1061     portnum = (mcp->offset % 0x6000) / 0x800;
1062     if (portnum >= ci->max_port)
1063         return ENXIO;
1064     pi = &ci->port[portnum];
1065     if (mcp->offset >= 0x6000)
1066         offset += 0x6000;           /* put back in MsgCfgDesc address offset */
1067     dph = (u_int32_t *) ((u_long) pi->reg + offset);
1068
1069     /* read of TX are from RAM image, since hardware returns FF */
1070     dpr = (u_int32_t *) ((u_long) pi->regram + offset);
1071     if (mcp->offset < 0x6000)       /* non MsgDesc Tbl accesses might require
1072                                      * RAM access */
1073     {
1074         if (offset >= 0x200 && offset < 0x380)
1075             ramread = 1;
1076         if (offset >= 0x10 && offset < 0x200)
1077             ramread = 1;
1078     }
1079     /* read register from RAM or hardware, depending... */
1080     if (ramread)
1081     {
1082         data = *dpr;
1083         //printk ("c4_musycc_rw: RAM addr %p  read data %x (portno %x offset %x RAM ramread %x)\n", dpr, data, portnum, offset, ramread); /* RLD DEBUG */
1084     } else
1085     {
1086         data = pci_read_32 ((u_int32_t *) dph);
1087         //printk ("c4_musycc_rw: REG addr %p  read data %x (portno %x offset %x RAM ramread %x)\n", dph, data, portnum, offset, ramread); /* RLD DEBUG */
1088     }
1089
1090
1091     if (mcp->RWportnum & 0x80)
1092     {                               /* control says this is a register
1093                                      * _write_ */
1094         if (mcp->value == data)
1095             printk ("%s: musycc grp%d already that value! writing again anyhow.\n",
1096                     ci->devname, (mcp->RWportnum & 0x7));
1097         /* write register RAM */
1098         if (ramread)
1099             *dpr = mcp->value;
1100         /* write hardware register */
1101         pci_write_32 ((u_int32_t *) dph, mcp->value);
1102     }
1103     mcp->value = data;              /* return the read value (or the 'old
1104                                      * value', if is write) */
1105     return 0;
1106 }
1107
1108 status_t
1109 c4_get_port (ci_t * ci, int portnum)
1110 {
1111     if (portnum >= ci->max_port)    /* sanity check */
1112         return ENXIO;
1113
1114     SD_SEM_TAKE (&ci->sem_wdbusy, "_wd_");      /* only 1 thru here, per
1115                                                  * board */
1116     checkPorts (ci);
1117     ci->port[portnum].p.portStatus = (u_int8_t) ci->alarmed[portnum];
1118     ci->alarmed[portnum] &= 0xdf;
1119     SD_SEM_GIVE (&ci->sem_wdbusy);  /* release per-board hold */
1120     return 0;
1121 }
1122
1123 status_t
1124 c4_set_port (ci_t * ci, int portnum)
1125 {
1126     mpi_t      *pi;
1127     struct sbecom_port_param *pp;
1128     int         e1mode;
1129     u_int8_t    clck;
1130     int         i;
1131
1132     if (portnum >= ci->max_port)    /* sanity check */
1133         return ENXIO;
1134
1135     pi = &ci->port[portnum];
1136     pp = &ci->port[portnum].p;
1137     e1mode = IS_FRAME_ANY_E1 (pp->port_mode);
1138     if (log_level >= LOG_MONITOR2)
1139     {
1140         printk ("%s: c4_set_port[%d]:  entered, e1mode = %x, openchans %d.\n",
1141                 ci->devname,
1142                 portnum, e1mode, pi->openchans);
1143     }
1144     if (pi->openchans)
1145         return EBUSY;               /* group needs initialization only for
1146                                      * first channel of a group */
1147
1148 #if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,41)
1149     {
1150         status_t    ret;
1151
1152         if ((ret = c4_wq_port_init (pi)))       /* create/init
1153                                                  * workqueue_struct */
1154             return (ret);
1155     }
1156 #endif
1157
1158     init_comet (ci, pi->cometbase, pp->port_mode, 1 /* clockmaster == true */ , pp->portP);
1159     clck = pci_read_32 ((u_int32_t *) &ci->cpldbase->mclk) & PMCC4_CPLD_MCLK_MASK;
1160     if (e1mode)
1161         clck |= 1 << portnum;
1162     else
1163         clck &= 0xf ^ (1 << portnum);
1164
1165     pci_write_32 ((u_int32_t *) &ci->cpldbase->mclk, clck);
1166     pci_write_32 ((u_int32_t *) &ci->cpldbase->mcsr, PMCC4_CPLD_MCSR_IND);
1167     pci_write_32 ((u_int32_t *) &pi->reg->gbp, OS_vtophys (pi->regram));
1168
1169     /*********************************************************************/
1170     /* ERRATA: If transparent mode is used, do not set OOFMP_DISABLE bit */
1171     /*********************************************************************/
1172
1173     pi->regram->grcd =
1174         __constant_cpu_to_le32 (MUSYCC_GRCD_RX_ENABLE |
1175                                 MUSYCC_GRCD_TX_ENABLE |
1176                                 MUSYCC_GRCD_OOFMP_DISABLE |
1177                                 MUSYCC_GRCD_SF_ALIGN |  /* per MUSYCC ERRATA,
1178                                                          * for T1 * fix */
1179                                 MUSYCC_GRCD_COFAIRQ_DISABLE |
1180                                 MUSYCC_GRCD_MC_ENABLE |
1181                        (MUSYCC_GRCD_POLLTH_32 << MUSYCC_GRCD_POLLTH_SHIFT));
1182
1183     pi->regram->pcd =
1184         __constant_cpu_to_le32 ((e1mode ? 1 : 0) |
1185                                 MUSYCC_PCD_TXSYNC_RISING |
1186                                 MUSYCC_PCD_RXSYNC_RISING |
1187                                 MUSYCC_PCD_RXDATA_RISING);
1188
1189     /* Message length descriptor */
1190     pi->regram->mld = __constant_cpu_to_le32 (max_mru | (max_mru << 16));
1191
1192     /* tsm algorithm */
1193     for (i = 0; i < 32; i++)
1194     {
1195
1196         /*** ASSIGNMENT NOTES:                             ***/
1197         /*** Group's channel  ZERO  unavailable if E1.     ***/
1198         /*** Group's channel  16    unavailable if E1 CAS. ***/
1199         /*** Group's channels 24-31 unavailable if T1.     ***/
1200
1201         if (((i == 0) && e1mode) ||
1202             ((i == 16) && ((pp->port_mode == CFG_FRAME_E1CRC_CAS) || (pp->port_mode == CFG_FRAME_E1CRC_CAS_AMI)))
1203             || ((i > 23) && (!e1mode)))
1204         {
1205             pi->tsm[i] = 0xff;      /* make tslot unavailable for this mode */
1206         } else
1207         {
1208             pi->tsm[i] = 0x00;      /* make tslot available for assignment */
1209         }
1210     }
1211     for (i = 0; i < MUSYCC_NCHANS; i++)
1212     {
1213         pi->regram->ttsm[i] = 0;
1214         pi->regram->rtsm[i] = 0;
1215     }
1216     FLUSH_MEM_WRITE ();
1217     musycc_serv_req (pi, SR_GROUP_INIT | SR_RX_DIRECTION);
1218     musycc_serv_req (pi, SR_GROUP_INIT | SR_TX_DIRECTION);
1219
1220     musycc_init_mdt (pi);
1221
1222     pi->group_is_set = 1;
1223     pi->p = *pp;
1224     return 0;
1225 }
1226
1227
1228 unsigned int max_int = 0;
1229
1230 status_t
1231 c4_new_chan (ci_t * ci, int portnum, int channum, void *user)
1232 {
1233     mpi_t      *pi;
1234     mch_t      *ch;
1235     int         gchan;
1236
1237     if (c4_find_chan (channum))     /* a new channel shouldn't already exist */
1238         return EEXIST;
1239
1240     if (portnum >= ci->max_port)    /* sanity check */
1241         return ENXIO;
1242
1243     pi = &(ci->port[portnum]);
1244     /* find any available channel within this port */
1245     for (gchan = 0; gchan < MUSYCC_NCHANS; gchan++)
1246     {
1247         ch = pi->chan[gchan];
1248         if (ch && ch->state == UNASSIGNED)      /* no assignment is good! */
1249             break;
1250     }
1251     if (gchan == MUSYCC_NCHANS)     /* exhausted table, all were assigned */
1252         return ENFILE;
1253
1254     ch->up = pi;
1255
1256     /* NOTE: mch_t already cleared during OS_kmalloc() */
1257     ch->state = DOWN;
1258     ch->user = user;
1259     ch->gchan = gchan;
1260     ch->channum = channum;          /* mark our channel assignment */
1261     ch->p.channum = channum;
1262 #if 1
1263     ch->p.card = ci->brdno;
1264     ch->p.port = portnum;
1265 #endif
1266     ch->p.chan_mode = CFG_CH_PROTO_HDLC_FCS16;
1267     ch->p.idlecode = CFG_CH_FLAG_7E;
1268     ch->p.pad_fill_count = 2;
1269     spin_lock_init (&ch->ch_rxlock);
1270     spin_lock_init (&ch->ch_txlock);
1271
1272 #if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,41)
1273     {
1274         status_t    ret;
1275
1276         if ((ret = c4_wk_chan_init (pi, ch)))
1277             return ret;
1278     }
1279 #endif
1280
1281     /* save off interface assignments which bound a board */
1282     if (ci->first_if == 0)          /* first channel registered is assumed to
1283                                      * be the lowest channel */
1284     {
1285         ci->first_if = ci->last_if = user;
1286         ci->first_channum = ci->last_channum = channum;
1287     } else
1288     {
1289         ci->last_if = user;
1290         if (ci->last_channum < channum) /* higher number channel found */
1291             ci->last_channum = channum;
1292     }
1293     return 0;
1294 }
1295
1296 status_t
1297 c4_del_chan (int channum)
1298 {
1299     mch_t      *ch;
1300
1301     if (!(ch = c4_find_chan (channum)))
1302         return ENOENT;
1303     if (ch->state == UP)
1304         musycc_chan_down ((ci_t *) 0, channum);
1305     ch->state = UNASSIGNED;
1306     ch->gchan = (-1);
1307     ch->channum = (-1);
1308     ch->p.channum = (-1);
1309     return 0;
1310 }
1311
1312 status_t
1313 c4_del_chan_stats (int channum)
1314 {
1315     mch_t      *ch;
1316
1317     if (!(ch = c4_find_chan (channum)))
1318         return ENOENT;
1319
1320     memset (&ch->s, 0, sizeof (struct sbecom_chan_stats));
1321     return 0;
1322 }
1323
1324
1325 status_t
1326 c4_set_chan (int channum, struct sbecom_chan_param * p)
1327 {
1328     mch_t      *ch;
1329     int         i, x = 0;
1330
1331     if (!(ch = c4_find_chan (channum)))
1332         return ENOENT;
1333
1334 #if 1
1335     if (ch->p.card != p->card ||
1336         ch->p.port != p->port ||
1337         ch->p.channum != p->channum)
1338         return EINVAL;
1339 #endif
1340
1341     if (!(ch->up->group_is_set))
1342     {
1343         return EIO;                 /* out of order, SET_PORT command
1344                                      * required prior to first group's
1345                                      * SET_CHAN command */
1346     }
1347     /*
1348      * Check for change of parameter settings in order to invoke closing of
1349      * channel prior to hardware poking.
1350      */
1351
1352     if (ch->p.status != p->status || ch->p.chan_mode != p->chan_mode ||
1353         ch->p.data_inv != p->data_inv || ch->p.intr_mask != p->intr_mask ||
1354         ch->txd_free < ch->txd_num) /* to clear out queued messages */
1355         x = 1;                      /* we have a change requested */
1356     for (i = 0; i < 32; i++)        /* check for timeslot mapping changes */
1357         if (ch->p.bitmask[i] != p->bitmask[i])
1358             x = 1;                  /* we have a change requested */
1359     ch->p = *p;
1360     if (x && (ch->state == UP))     /* if change request and channel is
1361                                      * open... */
1362     {
1363         status_t    ret;
1364
1365         if ((ret = musycc_chan_down ((ci_t *) 0, channum)))
1366             return ret;
1367         if ((ret = c4_chan_up (ch->up->up, channum)))
1368             return ret;
1369         sd_enable_xmit (ch->user);  /* re-enable to catch flow controlled
1370                                      * channel */
1371     }
1372     return 0;
1373 }
1374
1375
1376 status_t
1377 c4_get_chan (int channum, struct sbecom_chan_param * p)
1378 {
1379     mch_t      *ch;
1380
1381     if (!(ch = c4_find_chan (channum)))
1382         return ENOENT;
1383     *p = ch->p;
1384     return 0;
1385 }
1386
1387 status_t
1388 c4_get_chan_stats (int channum, struct sbecom_chan_stats * p)
1389 {
1390     mch_t      *ch;
1391
1392     if (!(ch = c4_find_chan (channum)))
1393         return ENOENT;
1394     *p = ch->s;
1395     p->tx_pending = atomic_read (&ch->tx_pending);
1396     return 0;
1397 }
1398
1399 STATIC int
1400 c4_fifo_alloc (mpi_t * pi, int chan, int *len)
1401 {
1402     int         i, l = 0, start = 0, max = 0, maxstart = 0;
1403
1404     for (i = 0; i < 32; i++)
1405     {
1406         if (pi->fifomap[i] != -1)
1407         {
1408             l = 0;
1409             start = i + 1;
1410             continue;
1411         }
1412         ++l;
1413         if (l > max)
1414         {
1415             max = l;
1416             maxstart = start;
1417         }
1418         if (max == *len)
1419             break;
1420     }
1421     if (max != *len)
1422     {
1423         if (log_level >= LOG_WARN)
1424             printk (
1425                   "%s: wanted to allocate %d fifo space, but got only %d\n",
1426                     pi->up->devname, *len, max);
1427         *len = max;
1428     }
1429     if (log_level >= LOG_DEBUG)
1430         printk ("%s: allocated %d fifo at %d for channel %d/%d\n",
1431                 pi->up->devname, max, start, chan, pi->p.portnum);
1432     for (i = maxstart; i < (maxstart + max); i++)
1433         pi->fifomap[i] = chan;
1434     return start;
1435 }
1436
1437 void
1438 c4_fifo_free (mpi_t * pi, int chan)
1439 {
1440     int         i;
1441
1442     if (log_level >= LOG_DEBUG)
1443         printk ("%s: deallocated fifo for channel %d/%d\n",
1444                 pi->up->devname, chan, pi->p.portnum);
1445     for (i = 0; i < 32; i++)
1446         if (pi->fifomap[i] == chan)
1447             pi->fifomap[i] = -1;
1448 }
1449
1450
1451 status_t
1452 c4_chan_up (ci_t * ci, int channum)
1453 {
1454     mpi_t      *pi;
1455     mch_t      *ch;
1456     struct mbuf *m;
1457     struct mdesc *md;
1458     int         nts, nbuf, txnum, rxnum;
1459     int         addr, i, j, gchan;
1460     u_int32_t   tmp;            /* for optimizing conversion across BE
1461                                  * platform */
1462
1463     if (!(ch = c4_find_chan (channum)))
1464         return ENOENT;
1465     if (ch->state == UP)
1466     {
1467         if (log_level >= LOG_MONITOR)
1468             printk ("%s: channel already UP, graceful early exit\n", ci->devname);
1469         return 0;
1470     }
1471     pi = ch->up;
1472     gchan = ch->gchan;
1473     /* find nts ('number of timeslots') */
1474     nts = 0;
1475     for (i = 0; i < 32; i++)
1476     {
1477         if (ch->p.bitmask[i] & pi->tsm[i])
1478         {
1479             if (1 || log_level >= LOG_WARN)
1480             {
1481                 printk ("%s: c4_chan_up[%d] EINVAL (attempt to cfg in-use or unavailable TimeSlot[%d])\n",
1482                         ci->devname, channum, i);
1483                 printk ("+ ask4 %x, currently %x\n", ch->p.bitmask[i], pi->tsm[i]);
1484             }
1485             return EINVAL;
1486         }
1487         for (j = 0; j < 8; j++)
1488             if (ch->p.bitmask[i] & (1 << j))
1489                 nts++;
1490     }
1491
1492     nbuf = nts / 8 ? nts / 8 : 1;
1493     if (!nbuf)
1494     {
1495         /* if( log_level >= LOG_WARN)  */
1496         printk ("%s: c4_chan_up[%d] ENOBUFS (no TimeSlots assigned)\n", ci->devname, channum);
1497         return ENOBUFS;             /* this should not happen */
1498     }
1499     addr = c4_fifo_alloc (pi, gchan, &nbuf);
1500     ch->state = UP;
1501
1502     /* Setup the Time Slot Map */
1503     musycc_update_timeslots (pi);
1504
1505     /* ch->tx_limit = nts; */
1506     ch->s.tx_pending = 0;
1507
1508     /* Set Channel Configuration Descriptors */
1509     {
1510         u_int32_t   ccd;
1511
1512         ccd = musycc_chan_proto (ch->p.chan_mode) << MUSYCC_CCD_PROTO_SHIFT;
1513         if ((ch->p.chan_mode == CFG_CH_PROTO_ISLP_MODE) ||
1514             (ch->p.chan_mode == CFG_CH_PROTO_TRANS))
1515         {
1516             ccd |= MUSYCC_CCD_FCS_XFER; /* Non FSC Mode */
1517         }
1518         ccd |= 2 << MUSYCC_CCD_MAX_LENGTH;      /* Select second MTU */
1519         ccd |= ch->p.intr_mask;
1520         ccd |= addr << MUSYCC_CCD_BUFFER_LOC;
1521         if (ch->p.chan_mode == CFG_CH_PROTO_TRANS)
1522             ccd |= (nbuf) << MUSYCC_CCD_BUFFER_LENGTH;
1523         else
1524             ccd |= (nbuf - 1) << MUSYCC_CCD_BUFFER_LENGTH;
1525
1526         if (ch->p.data_inv & CFG_CH_DINV_TX)
1527             ccd |= MUSYCC_CCD_INVERT_DATA;      /* Invert data */
1528         pi->regram->tcct[gchan] = cpu_to_le32 (ccd);
1529
1530         if (ch->p.data_inv & CFG_CH_DINV_RX)
1531             ccd |= MUSYCC_CCD_INVERT_DATA;      /* Invert data */
1532         else
1533             ccd &= ~MUSYCC_CCD_INVERT_DATA;     /* take away data inversion */
1534         pi->regram->rcct[gchan] = cpu_to_le32 (ccd);
1535         FLUSH_MEM_WRITE ();
1536     }
1537
1538     /* Reread the Channel Configuration Descriptor for this channel */
1539     musycc_serv_req (pi, SR_CHANNEL_CONFIG | SR_RX_DIRECTION | gchan);
1540     musycc_serv_req (pi, SR_CHANNEL_CONFIG | SR_TX_DIRECTION | gchan);
1541
1542     /*
1543      * Figure out how many buffers we want.  If the customer has changed from
1544      * the defaults, then use the changed values.  Otherwise, use Transparent
1545      * mode's specific minimum default settings.
1546      */
1547     if (ch->p.chan_mode == CFG_CH_PROTO_TRANS)
1548     {
1549         if (max_rxdesc_used == max_rxdesc_default)      /* use default setting */
1550             max_rxdesc_used = MUSYCC_RXDESC_TRANS;
1551         if (max_txdesc_used == max_txdesc_default)      /* use default setting */
1552             max_txdesc_used = MUSYCC_TXDESC_TRANS;
1553     }
1554     /*
1555      * Increase counts when hyperchanneling, since this implies an increase
1556      * in throughput per channel
1557      */
1558     rxnum = max_rxdesc_used + (nts / 4);
1559     txnum = max_txdesc_used + (nts / 4);
1560
1561 #if 0
1562     /* DEBUG INFO */
1563     if (log_level >= LOG_MONITOR)
1564         printk ("%s: mode %x rxnum %d (rxused %d def %d) txnum %d (txused %d def %d)\n",
1565                 ci->devname, ch->p.chan_mode,
1566                 rxnum, max_rxdesc_used, max_rxdesc_default,
1567                 txnum, max_txdesc_used, max_txdesc_default);
1568 #endif
1569
1570     ch->rxd_num = rxnum;
1571     ch->txd_num = txnum;
1572     ch->rxix_irq_srv = 0;
1573
1574     ch->mdr = OS_kmalloc (sizeof (struct mdesc) * rxnum);
1575     ch->mdt = OS_kmalloc (sizeof (struct mdesc) * txnum);
1576     if (ch->p.chan_mode == CFG_CH_PROTO_TRANS)
1577         tmp = __constant_cpu_to_le32 (max_mru | EOBIRQ_ENABLE);
1578     else
1579         tmp = __constant_cpu_to_le32 (max_mru);
1580
1581     for (i = 0, md = ch->mdr; i < rxnum; i++, md++)
1582     {
1583         if (i == (rxnum - 1))
1584         {
1585             md->snext = &ch->mdr[0];/* wrapness */
1586         } else
1587         {
1588             md->snext = &ch->mdr[i + 1];
1589         }
1590         md->next = cpu_to_le32 (OS_vtophys (md->snext));
1591
1592         if (!(m = OS_mem_token_alloc (max_mru)))
1593         {
1594             if (log_level >= LOG_MONITOR)
1595                 printk ("%s: c4_chan_up[%d] - token alloc failure, size = %d.\n", ci->devname, channum, max_mru);
1596             goto errfree;
1597         }
1598         md->mem_token = m;
1599         md->data = cpu_to_le32 (OS_vtophys (OS_mem_token_data (m)));
1600         md->status = tmp | MUSYCC_RX_OWNED;     /* MUSYCC owns RX descriptor **
1601                                                  * CODING NOTE:
1602                                                  * MUSYCC_RX_OWNED = 0 so no
1603                                                  * need to byteSwap */
1604     }
1605
1606     for (i = 0, md = ch->mdt; i < txnum; i++, md++)
1607     {
1608         md->status = HOST_TX_OWNED; /* Host owns TX descriptor ** CODING
1609                                      * NOTE: HOST_TX_OWNED = 0 so no need to
1610                                      * byteSwap */
1611         md->mem_token = 0;
1612         md->data = 0;
1613         if (i == (txnum - 1))
1614         {
1615             md->snext = &ch->mdt[0];/* wrapness */
1616         } else
1617         {
1618             md->snext = &ch->mdt[i + 1];
1619         }
1620         md->next = cpu_to_le32 (OS_vtophys (md->snext));
1621     }
1622     ch->txd_irq_srv = ch->txd_usr_add = &ch->mdt[0];
1623     ch->txd_free = txnum;
1624     ch->tx_full = 0;
1625     ch->txd_required = 0;
1626
1627     /* Configure it into the chip */
1628     tmp = cpu_to_le32 (OS_vtophys (&ch->mdt[0]));
1629     pi->regram->thp[gchan] = tmp;
1630     pi->regram->tmp[gchan] = tmp;
1631
1632     tmp = cpu_to_le32 (OS_vtophys (&ch->mdr[0]));
1633     pi->regram->rhp[gchan] = tmp;
1634     pi->regram->rmp[gchan] = tmp;
1635
1636     /* Activate the Channel */
1637     FLUSH_MEM_WRITE ();
1638     if (ch->p.status & RX_ENABLED)
1639     {
1640 #ifdef RLD_TRANS_DEBUG
1641         printk ("++ c4_chan_up() CHAN RX ACTIVATE: chan %d\n", ch->channum);
1642 #endif
1643         ch->ch_start_rx = 0;        /* we are restarting RX... */
1644         musycc_serv_req (pi, SR_CHANNEL_ACTIVATE | SR_RX_DIRECTION | gchan);
1645     }
1646     if (ch->p.status & TX_ENABLED)
1647     {
1648 #ifdef RLD_TRANS_DEBUG
1649         printk ("++ c4_chan_up() CHAN TX ACTIVATE: chan %d <delayed>\n", ch->channum);
1650 #endif
1651         ch->ch_start_tx = CH_START_TX_1ST;      /* we are delaying start
1652                                                  * until receipt from user of
1653                                                  * first packet to transmit. */
1654     }
1655     ch->status = ch->p.status;
1656     pi->openchans++;
1657     return 0;
1658
1659 errfree:
1660     while (i > 0)
1661     {
1662         /* Don't leak all the previously allocated mbufs in this loop */
1663         i--;
1664         OS_mem_token_free (ch->mdr[i].mem_token);
1665     }
1666     OS_kfree (ch->mdt);
1667     ch->mdt = 0;
1668     ch->txd_num = 0;
1669     OS_kfree (ch->mdr);
1670     ch->mdr = 0;
1671     ch->rxd_num = 0;
1672     ch->state = DOWN;
1673     return ENOBUFS;
1674 }
1675
1676 /* stop the hardware from servicing & interrupting */
1677
1678 void
1679 c4_stopwd (ci_t * ci)
1680 {
1681     OS_stop_watchdog (&ci->wd);
1682     SD_SEM_TAKE (&ci->sem_wdbusy, "_stop_");    /* ensure WD not running */
1683     SD_SEM_GIVE (&ci->sem_wdbusy);
1684 }
1685
1686
1687 void
1688 sbecom_get_brdinfo (ci_t * ci, struct sbe_brd_info * bip, u_int8_t *bsn)
1689 {
1690     char       *np;
1691     u_int32_t   sn = 0;
1692     int         i;
1693
1694     bip->brdno = ci->brdno;         /* our board number */
1695     bip->brd_id = ci->brd_id;
1696     bip->brd_hdw_id = ci->hdw_bid;
1697     bip->brd_chan_cnt = MUSYCC_NCHANS * ci->max_port;   /* number of channels
1698                                                          * being used */
1699     bip->brd_port_cnt = ci->max_port;   /* number of ports being used */
1700     bip->brd_pci_speed = BINFO_PCI_SPEED_unk;   /* PCI speed not yet
1701                                                  * determinable */
1702
1703     if (ci->first_if)
1704     {
1705 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
1706         np = (char *) hdlc_to_name (ci->first_if);
1707 #else
1708         {
1709             struct net_device *dev;
1710
1711             dev = (struct net_device *) ci->first_if;
1712             np = (char *) dev->name;
1713         }
1714 #endif
1715         strncpy (bip->first_iname, np, CHNM_STRLEN - 1);
1716     } else
1717         strcpy (bip->first_iname, "<NULL>");
1718     if (ci->last_if)
1719     {
1720 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
1721         np = (char *) hdlc_to_name (ci->last_if);
1722 #else
1723         {
1724             struct net_device *dev;
1725
1726             dev = (struct net_device *) ci->last_if;
1727             np = (char *) dev->name;
1728         }
1729 #endif
1730         strncpy (bip->last_iname, np, CHNM_STRLEN - 1);
1731     } else
1732         strcpy (bip->last_iname, "<NULL>");
1733
1734     if (bsn)
1735     {
1736         for (i = 0; i < 3; i++)
1737         {
1738             bip->brd_mac_addr[i] = *bsn++;
1739         }
1740         for (; i < 6; i++)
1741         {
1742             bip->brd_mac_addr[i] = *bsn;
1743             sn = (sn << 8) | *bsn++;
1744         }
1745     } else
1746     {
1747         for (i = 0; i < 6; i++)
1748             bip->brd_mac_addr[i] = 0;
1749     }
1750     bip->brd_sn = sn;
1751 }
1752
1753
1754 status_t
1755 c4_get_iidinfo (ci_t * ci, struct sbe_iid_info * iip)
1756 {
1757     struct net_device *dev;
1758     char       *np;
1759
1760     if (!(dev = getuserbychan (iip->channum)))
1761         return ENOENT;
1762
1763 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
1764     np = (char *) hdlc_to_name (dev_to_hdlc (dev));
1765 #else
1766     np = dev->name;
1767 #endif
1768     strncpy (iip->iname, np, CHNM_STRLEN - 1);
1769     return 0;
1770 }
1771
1772
1773 #ifdef CONFIG_SBE_PMCC4_NCOMM
1774 void        (*nciInterrupt[MAX_BOARDS][4]) (void);
1775 extern void wanpmcC4T1E1_hookInterrupt (int cardID, int deviceID, void *handler);
1776
1777 void
1778 wanpmcC4T1E1_hookInterrupt (int cardID, int deviceID, void *handler)
1779 {
1780     if (cardID < MAX_BOARDS)    /* sanity check */
1781         nciInterrupt[cardID][deviceID] = handler;
1782 }
1783
1784 irqreturn_t
1785 c4_ebus_intr_th_handler (void *devp)
1786 {
1787     ci_t       *ci = (ci_t *) devp;
1788     volatile u_int32_t ists;
1789     int         handled = 0;
1790     int         brdno;
1791
1792     /* which COMET caused the interrupt */
1793     brdno = ci->brdno;
1794     ists = pci_read_32 ((u_int32_t *) &ci->cpldbase->intr);
1795     if (ists & PMCC4_CPLD_INTR_CMT_1)
1796     {
1797         handled = 0x1;
1798         if (nciInterrupt[brdno][0] != NULL)
1799             (*nciInterrupt[brdno][0]) ();
1800     }
1801     if (ists & PMCC4_CPLD_INTR_CMT_2)
1802     {
1803         handled |= 0x2;
1804         if (nciInterrupt[brdno][1] != NULL)
1805             (*nciInterrupt[brdno][1]) ();
1806     }
1807     if (ists & PMCC4_CPLD_INTR_CMT_3)
1808     {
1809         handled |= 0x4;
1810         if (nciInterrupt[brdno][2] != NULL)
1811             (*nciInterrupt[brdno][2]) ();
1812     }
1813     if (ists & PMCC4_CPLD_INTR_CMT_4)
1814     {
1815         handled |= 0x8;
1816         if (nciInterrupt[brdno][3] != NULL)
1817             (*nciInterrupt[brdno][3]) ();
1818     }
1819 #if 0
1820     /*** Test code just de-implements the asserted interrupt.  Alternate
1821     vendor will supply COMET interrupt handling code herein or such.
1822     ***/
1823     pci_write_32 ((u_int32_t *) &ci->reg->glcd, GCD_MAGIC | MUSYCC_GCD_INTB_DISABLE);
1824 #endif
1825
1826 #if LINUX_VERSION_CODE <= KERNEL_VERSION(2,4,20)
1827     return;
1828 #else
1829     return IRQ_RETVAL (handled);
1830 #endif
1831 }
1832
1833
1834 unsigned long
1835 wanpmcC4T1E1_getBaseAddress (int cardID, int deviceID)
1836 {
1837     ci_t       *ci;
1838     unsigned long base = 0;
1839
1840     ci = c4_list;
1841     while (ci)
1842     {
1843         if (ci->brdno == cardID)    /* found valid device */
1844         {
1845             if (deviceID < ci->max_port)        /* comet is supported */
1846                 base = ((unsigned long) ci->port[deviceID].cometbase);
1847             break;
1848         }
1849         ci = ci->next;              /* next board, if any */
1850     }
1851     return (base);
1852 }
1853
1854 #endif                          /*** CONFIG_SBE_PMCC4_NCOMM ***/
1855
1856
1857 /***  End-of-File  ***/