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