/* ia_suni.c */

/* Front end interface initialization and error handling */

/* Copyright (c) 2000  James M. Westall, Dept of Computer Science,
 *                     Clemson University, Clemson SC 29634 USA
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * To obtain a copy of the GNU General Public License write to the
 * Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 * This software is derived from software developed by the Interphase
 * corporation and released by them in September 2000. The software
 * released by Interphase carried the following copyright notice:
 *
 *
 *                          Copyright (C) 1993
 *                Interphase Corporation, Dallas, TX 75234
 *                          All Rights Reserved
 *
 * This code contains confidential information and  trade  secrets  of
 * Interphase Corporation which shall not be reproduced or transferred
 * to other programs or disclosed to others or used for  manufacturing
 * or any other purpose without prior written permission of Interphase
 * Corporation.  Use of copyright notice is precautionary and does not
 * imply publication or intent thereof.
 */

/*H
 * FILE: ia_suni.c
 *
 * DESCRIPTION:
 *   This file contains the SUNI code.
 *
 * FUNCTIONS:
 * EXPORTED:
 *   see below.
 *
 * STATIC:
 *   see below.
 *
 * GLOBAL DATA:
 *
 */

#include "ia_defs.h"

/*
 * Exported routines
 */
void ia_suni_init (ia_softc_t *);
void ia_suni_intr (ia_softc_t *);
void ia_suni_timer (void *);
void ia_suni_pm7345_timer (void *);
void ia_suni_framing (ia_softc_t *, int);


/*
 * Local routines
 */


#define BOL(x)  ( (x) ? 1 : 0 )

extern u_int   ia_stm;


/*F
 * FUNCTION: ia_suni_init
 *
 * DESCRIPTION:
 * This routine is called to initialize SUNI.
 *
 * ARGUMENTS:
 * softc    Device state structure
 *
 * ENVIRONMENT:
 * Called from ia_attach
 *
 * RETURN:
 * Nothing
 *
 * NOTES:
 */

/**/
void
ia_suni_init (ia_softc_t *softc)
{
   ia_suni_t   *suni = softc->brd_regs.suni;
   uint_t      val;
   uint_t      carrier_detect;

   /*
    * Disable cell filtering in RACP processors
    * Pre-Altantic only.
    */
   printk("IA 5515: Entering pre-Atlantic SUNI init \n");
   ia_or32 (&suni->suni_racp_cs, SUNI_PASS);

   /*
    * Initialize carrier detect state
    */
   val = ia_get32 (&suni->suni_master_intr_stat);
   carrier_detect = BOL (val & SUNI_GPINV);

   softc->suni_carrier = 0;

   /*
    * Enable GPIN interrupt
    */
   ia_or32 (&suni->suni_master_config, SUNI_GPINE);

   /*
    * Clear error counters
    */
   ia_put32 (&suni->suni_master_reset, 0);

   /*
    * Clear "PMCTST" in master test register.
    */
   ia_put32 (&suni->suni_master_test, 0);

   /*
    * Set to STM mode if specified
    * S[1] = 1
    */
   if (ia_stm) {
      ia_or32 (&suni->suni_tpop_arb_prtm, 0x08);

      /*
       * Set SUNI to transmit IDLE cells when no data cells
       * to transmit
       */
      ia_put32 (&suni->suni_tacp_idle_hdr_pat, 1);
   }
   /*jmw_test*/
/* ia_put32(&suni->suni_master_control, SUNI_DLE | SUNI_LOOPT); */

}
#if 0

/*F
 * FUNCTION: ia_suni_intr
 *
 * DESCRIPTION:
 * This routine is called to process SUNI interrupts.
 *
 * ARGUMENTS:
 * softc    Device state structure
 *
 * ENVIRONMENT:
 * Called from ia_intr
 *
 * RETURN:
 * Nothing
 *
 * NOTES:
 * Called when carries detect state changes.
 */
void
ia_suni_intr (ia_softc_t *softc)
{
   u_int           frmr_intr;
   ia_suni_t   *suni = softc->brd_regs.suni;
   ia_suni_stat_t  *suni_stat = &softc->suni_stat;
   suni_pm7345_t *suni_pm7345 = softc->brd_regs.suni_pm7345;
   uint_t      val;

   mutex_enter (&softc->ia_misc_mutex);

   if (softc->atlantic) {
      if (softc->phy_type == PHY_DS3) {
         /*
          * clear FRMR interrupts
          */
         frmr_intr   = ia_get32 (&suni_pm7345->suni_ds3_frm_intr_stat);

         val = ia_get32 (&suni_pm7345->suni_ds3_frm_stat);
         suni_stat->carrier_detect = BOL (!(val & SUNI_DS3_LOSV));
      }
      else if (softc->phy_type == PHY_E3) {
         /*
          * clear FRMR interrupts
          */
         frmr_intr   = ia_get32 (&suni_pm7345->suni_e3_frm_maint_intr_ind);

         val = ia_get32 (&suni_pm7345->suni_e3_frm_fram_intr_ind_stat);
         suni_stat->carrier_detect = BOL (!(val & SUNI_E3_LOS));
      }
      else {
         val = ia_get32 (&suni->suni_rsop_status);
         suni_stat->carrier_detect = BOL (!(val & SUNI_LOSV));
      }
   }
   else {
      val = ia_get32 (&suni->suni_master_intr_stat);
      suni_stat->carrier_detect = BOL (val & SUNI_GPINV);
   }

#ifdef __SNMP__
   if ((softc->phy_type == PHY_DS3) || (softc->phy_type == PHY_E3)) {
      val = ia_get32 (&suni_pm7345->suni_rxcp_intr_en_sts);
      if (val & SUNI_DS3_OOCDI)
         if (ia_get32 (&suni_pm7345->suni_rxcp_ctrl) & SUNI_DS3_OOCDV) {
            suni_stat->sync_to_hunt++;
            /*
             * TBD : When do we reset it ??
             */
            if(suni_stat->sync_to_hunt >= SYNC_TO_HUNT_THRESHOLD)
               suni_pm7345->suni_rxcp_intr_en_sts &= ~SUNI_DS3_OOCDE;
         }
   }
   else {
      val = ia_get32 (&suni->suni_racp_intr);
      if (val & SUNI_OOCDI)
         if (ia_get32 (&suni->suni_racp_cs) & SUNI_OOCDV) {
            suni_stat->sync_to_hunt++;
            /*
             * TBD : When do we reset it ??
             */
            if (suni_stat->sync_to_hunt >= SYNC_TO_HUNT_THRESHOLD)
               suni->suni_racp_intr &= ~SUNI_OOCDE;
         }
   }
#endif __SNMP__

   softc->suni_carrier = 0;

   mutex_exit (&softc->ia_misc_mutex);
}

/*F
 * FUNCTION: ia_suni_timer
 *
 * DESCRIPTION:
 * This routine is called to gather SUNI statistics.
 *
 * ARGUMENTS:
 * softc    Device state structure
 *
 * ENVIRONMENT:
 * Called from ia_intr
 *
 * RETURN:
 * Nothing
 *
 * NOTES:
 * Need to read interrupt enable/status register first in each
 * block. Otherwise some interrupt status bits get cleared.
 */
void
ia_suni_timer (void *softcp)
{
   ia_softc_t *softc = softcp;
   ia_suni_t   *suni = softc->brd_regs.suni;
   ia_suni_stat_t  *suni_stat = &softc->suni_stat;
   uint_t      rsop_st;
   uint_t      rlop_cs;
   uint_t      rpop_cs;
   uint_t      racp_cs;
   uint_t      racp_intr;

   mutex_enter (&softc->ia_misc_mutex);

   /*
    * Load error counters
    */
#ifndef __RLS__
   ia_put32 (&suni->suni_master_reset, 0);
#else
        if (softc->atlantic)
                ia_put32 (&suni->suni_master_reset, 0);
        else
                ia_put32 (&suni->suni_rpop_bip8l, 0);
#endif /* __RLS__ */

   drv_usecwait ((clock_t)1);

   /*
    * RSOP stats
    */
   rsop_st   = ia_get32 (&suni->suni_rsop_status);

   if (softc->suni_carrier) {
      suni_stat->rsop_bse +=
         (ia_get32 (&suni->suni_rsop_section_bip8l) & 0xff) +
         ((ia_get32 (&suni->suni_rsop_section_bip8m) & 0xff) << 8);
   }

        suni_stat->rsop_oofe_state = BOL(rsop_st & SUNI_OOFV);
        suni_stat->rsop_lofe_state = BOL(rsop_st & SUNI_LOFV);
        suni_stat->rsop_lose_state = BOL(rsop_st & SUNI_LOSV);

   /*
    * RLOP stats
    */
   rlop_cs   = ia_get32 (&suni->suni_rlop_cs);

   if (softc->suni_carrier) {
      suni_stat->rlop_febe +=
         (ia_get32 (&suni->suni_rlop_line_febel) & 0xff) +
         ((ia_get32 (&suni->suni_rlop_line_febe) & 0xff) << 8) +
         ((ia_get32 (&suni->suni_rlop_line_febem) & 0x0f) << 16);

      suni_stat->rlop_lbe +=
         (ia_get32 (&suni->suni_rlop_line_bip24l) & 0xff) +
         ((ia_get32 (&suni->suni_rlop_line_bip24) & 0xff) << 8) +
         ((ia_get32 (&suni->suni_rlop_line_bip24m) & 0x0f) << 16);
   }

   suni_stat->rlop_ferf_state = BOL(rlop_cs & SUNI_FERFV);
   suni_stat->rlop_lais_state = BOL(rlop_cs & SUNI_LAISI);

   /*
    * RPOP interrupt
    */
   rpop_cs   = ia_get32 (&suni->suni_rpop_cs);

   suni_stat->rpop_psig = ia_get32 (&suni->suni_rpop_path_sig) & 0xff;

   if (softc->suni_carrier) {
      suni_stat->rpop_bip_count +=
         (ia_get32 (&suni->suni_rpop_bip8l) & 0xff) +
         ((ia_get32 (&suni->suni_rpop_bip8m) & 0xff) << 8);

      suni_stat->rpop_febe_count +=
         (ia_get32 (&suni->suni_rpop_febel) & 0xff) +
         ((ia_get32 (&suni->suni_rpop_febem) & 0xff) << 8);
   }

   suni_stat->rpop_lop_state  = BOL(rpop_cs & SUNI_LOP);
   suni_stat->rpop_pais_state = BOL(rpop_cs & SUNI_PAIS);
   suni_stat->rpop_pyel_state = BOL(rpop_cs & SUNI_PYEL);

   /*
    * RACP interrupt
    */
   racp_intr = ia_get32 (&suni->suni_racp_intr);
   racp_cs   = ia_get32 (&suni->suni_racp_cs);

   suni_stat->racp_hunt_presync = BOL(racp_cs & SUNI_OOCDV);

   if (racp_intr & SUNI_FUDRI)
      suni_stat->racp_fudr_count++;
   if (racp_intr & SUNI_FOVRI)
      suni_stat->racp_fovr_count++;


   if (softc->suni_carrier) {
      suni_stat->racp_chcs +=
            ia_get32 (&suni->suni_racp_corr_hcs) & 0xff;
      suni_stat->racp_uchcs +=
            ia_get32 (&suni->suni_racp_uncorr_hcs) & 0xff;
   }

   if (suni_stat->carrier_detect)
      softc->suni_carrier = 1;

#if defined(__SNMP__) || defined(__RLS__)
        if ((suni_stat->rsop_bse - suni_stat->old_rsop_bse) ||
                        (suni_stat->rsop_oofe_state) ||
                        (suni_stat->rsop_lofe_state) ||
                        (suni_stat->rsop_lose_state))
                suni_stat->sectionESs++;

        if (((suni_stat->rsop_bse - suni_stat->old_rsop_bse) >= 16) ||
                        (suni_stat->rsop_oofe_state) ||
                        (suni_stat->rsop_lofe_state) ||
                        (suni_stat->rsop_lose_state))
                suni_stat->sectionSESs++;

#ifdef  __RLS__
        if ((suni_stat->rsop_bse - suni_stat->old_rsop_bse) >= 16)
                suni_stat->sectionContiguousCVS++;
        else
                suni_stat->sectionContiguousCVS = 0;
#endif  /* __RLS__ */

        if (suni_stat->rsop_oofe_state)
                suni_stat->sectionSEFSs++;

        suni_stat->sectionCVs = suni_stat->rsop_bse;

#ifdef  __RLS__
        /*
         * Count the number of contiguous LOS and LOF seconds.
         * If we hit 3, the link has failed. Actually, according to
         * rfc1595, it should be 2.5 +/- 0.5. Since our  granularity is
         * a second, we do it at 3.
         */
        if (suni_stat->rsop_lose_state)
                suni_stat->sectionContiguousLOSES++;
        else
                suni_stat->sectionContiguousLOSES = 0;

        if (suni_stat->rsop_lofe_state)
                suni_stat->sectionContiguousLOFES++;
        else
                suni_stat->sectionContiguousLOFES = 0;

        if ((suni_stat->sectionContiguousLOSES >= 3) ||
                        (suni_stat->sectionContiguousLOFES >= 3) ||
                        (suni_stat->sectionContiguousCVS >= 10))
                suni_stat->failure = 1;
#endif  /* __RLS__ */

        if (!suni_stat->lineUnAvailableState)
        {
                if ((suni_stat->rlop_lbe - suni_stat->old_rlop_lbe) ||
                        (suni_stat->rlop_lais_state))
                                suni_stat->lineESs++;
                if (((suni_stat->rlop_lbe - suni_stat->old_rlop_lbe) >= 32) ||
                        (suni_stat->rlop_lais_state))
                {
                        suni_stat->lineSESs++;
                        suni_stat->lineContiguousSESs++;
                        if (suni_stat->lineContiguousSESs == 10)
                        {
                                suni_stat->lineUASs += 10;
                                suni_stat->lineUnAvailableState = 1;
                        }
                }
                else
                        suni_stat->lineContiguousSESs = 0;
                suni_stat->lineCVs = suni_stat->rlop_lbe;
        }
        else
        {
                if (((suni_stat->rlop_lbe - suni_stat->old_rlop_lbe) < 32) &&
                        (!suni_stat->rlop_lais_state))
                        suni_stat->lineContiguousNoSESs++;
                else
                {
                        suni_stat->lineUASs +=
                                (suni_stat->lineContiguousNoSESs > 0) ?
                                        suni_stat->lineContiguousNoSESs : 1;
                        suni_stat->lineContiguousNoSESs = 0;

                }
                if (suni_stat->lineContiguousNoSESs == 10)
                        suni_stat->lineContiguousNoSESs = suni_stat->lineUnAvailableState = 0;
        }

#ifdef  __RLS__
        /*
         * Check for contiguous coding violations only if
         * is a possibility of such.
         */
        if ((suni_stat->rlop_lbe - suni_stat->old_rlop_lbe) >= 32)
                suni_stat->lineContiguousCVS++;
        else
                suni_stat->lineContiguousCVS = 0;
#endif  /* __RLS__ */

        if ((suni_stat->rlop_febe - suni_stat->old_rlop_febe) ||
                                (suni_stat->rlop_ferf_state))
                suni_stat->flESs++;
        if (((suni_stat->rlop_febe - suni_stat->old_rlop_febe) >= 32) ||
                                (suni_stat->rlop_ferf_state))
                suni_stat->flSESs++;
        suni_stat->flCVs = suni_stat->rlop_febe;

#ifdef  __RLS__
        /*
         * Count the number of contiguous LAIS and FERF seconds.
         * 21 LAIS or 3 FERF indicate a failure, or there has been more
         * than 10 contiguous CV seconds.
         */
        if (suni_stat->rlop_lais_state)
                suni_stat->lineContiguousLAISS++;
        else
                suni_stat->lineContiguousLAISS = 0;

        if (suni_stat->rlop_ferf_state)
                suni_stat->lineContiguousFERFS++;
        else
                suni_stat->lineContiguousFERFS = 0;

        if ( (suni_stat->lineContiguousLAISS >= 21) ||
                        (suni_stat->lineContiguousFERFS >= 3) ||
                        (suni_stat->lineContiguousCVS >= 10) )
                suni_stat->failure = 1;
#endif  /* __RLS__ */

        if (!suni_stat->pathUnAvailableState)
        {
                if ((suni_stat->rpop_bip_count - suni_stat->old_rpop_bip_count)
||
                                (suni_stat->rpop_lop_state) ||
                                (suni_stat->rpop_pais_state) ||
                                (suni_stat->rpop_pyel_state))
                        suni_stat->pathESs++;
                if (((suni_stat->rpop_bip_count - suni_stat->old_rpop_bip_count) >= 16)||
                        (suni_stat->rpop_lop_state) ||
                        (suni_stat->rpop_pais_state) ||
                        (suni_stat->rpop_pyel_state))
                {
                        suni_stat->pathSESs++;
                        suni_stat->pathContiguousSESs++;
                        if (suni_stat->pathContiguousSESs == 10)
                        {
                                suni_stat->pathUASs += 10;
                                suni_stat->pathUnAvailableState = 1;
                        }
                }
                else
                        suni_stat->pathContiguousSESs = 0;
                suni_stat->pathCVs = suni_stat->rpop_bip_count;
        }
        else
        {
                if (((suni_stat->rpop_bip_count - suni_stat->old_rpop_bip_count) < 16)&&
                                (!suni_stat->rpop_lop_state) &&
                                (!suni_stat->rpop_pais_state) &&
                                (!suni_stat->rpop_pyel_state))
                        suni_stat->pathContiguousNoSESs++;
                else
                {
                        suni_stat->pathUASs +=
                                (suni_stat->pathContiguousNoSESs > 0) ?
                                        suni_stat->pathContiguousNoSESs : 1;
                        suni_stat->pathContiguousNoSESs = 0;
                }
                if (suni_stat->pathContiguousNoSESs == 10)
                        suni_stat->pathContiguousNoSESs = suni_stat->pathUnAvailableState=0;
        }

#ifdef  __RLS__
        /*
         * Check for contiguous FEBE only if there is a
         * possibility that it has occured.
         */
        if ((suni_stat->rpop_bip_count - suni_stat->old_rpop_bip_count) >= 16)
                suni_stat->pathContiguousFEBES++;
        else
                suni_stat->pathContiguousFEBES = 0;
#endif  /* __RLS__ */

        if (suni_stat->rpop_febe_count - suni_stat->old_rpop_febe_count)
                        suni_stat->fpESs++;
        if ((suni_stat->rpop_febe_count - suni_stat->old_rpop_febe_count) >= 16)                        suni_stat->fpSESs++;
        suni_stat->fpCVs = suni_stat->rpop_febe_count;

#ifdef  __RLS__
        /*
         * Count the number of contiguous LOP, PAIS and FEBE seconds.
         * 3 such seconds indicate failure.
         */
        if (suni_stat->rpop_lop_state)
                suni_stat->pathContiguousLOPS++;
        else
                suni_stat->pathContiguousLOPS = 0;

        if (suni_stat->rpop_pais_state)
                suni_stat->pathContiguousPAISS++;
        else
                suni_stat->pathContiguousPAISS = 0;

        if ( (suni_stat->pathContiguousLOPS >= 3) ||
                        (suni_stat->pathContiguousPAISS >= 3) ||
                        (suni_stat->pathContiguousFEBES >= 3) )
                suni_stat->failure = 1;
#endif  /* __RLS__ */

        suni_stat->old_rsop_bse = suni_stat->rsop_bse;
        suni_stat->old_rlop_lbe = suni_stat->rlop_lbe;
        suni_stat->old_rlop_febe = suni_stat->rlop_febe;
        suni_stat->old_rpop_bip_count = suni_stat->rpop_bip_count;
        suni_stat->old_rpop_febe_count = suni_stat->rpop_febe_count;

        if (suni_stat->failure) {
                suni_stat->failure = 0;
                ia_rollover (softc, 0, 0);      /* Link Down */
        }

        if (    (suni_stat->sectionContiguousLOSES) ||
                (suni_stat->sectionContiguousLOFES) ||
                (suni_stat->sectionContiguousCVS) ||
                (suni_stat->lineContiguousLAISS) ||
                (suni_stat->lineContiguousFERFS) ||
                (suni_stat->lineContiguousCVS) ||
                (suni_stat->pathContiguousLOPS) ||
                (suni_stat->pathContiguousPAISS) ||
                (suni_stat->pathContiguousFEBES)
        )
                suni_stat->no_failure_secs = 0;
        else
                suni_stat->no_failure_secs++;

        if (suni_stat->no_failure_secs >= 10)
                ia_rollover (softc, 1, 0);      /* Link Up */

#endif /* __SNMP__ || __RLS__ */

   mutex_exit (&softc->ia_misc_mutex);

   softc->suni_timeout =
      timeout (ia_suni_timer, (void *)softc, drv_usectohz(1000000));
}

/*F
 * FUNCTION: ia_suni_pm7345_timer
 *
 * DESCRIPTION:
 * This routine is called to gather SUNI statistics.
 *
 * ARGUMENTS:
 * softc    Device state structure
 *
 * ENVIRONMENT:
 * Called from ia_intr
 *
 * RETURN:
 * Nothing
 *
 * NOTES:
 * Need to read interrupt enable/status register first in each
 * block. Otherwise some interrupt status bits get cleared.
 */
void
ia_suni_pm7345_timer (void *softcp)
{
   ia_softc_t *softc = softcp;
   Vol suni_pm7345_t *suni_pm7345 = softc->brd_regs.suni_pm7345;
   ia_suni_stat_t  *suni_stat = &softc->suni_stat;
   u_int    frmr_st;
   u_int    e3_fram_st;
   u_int    e3_maint_st;
   u_int    rxcp_cs;
   u_int    rxcp_intr;

   mutex_enter (&softc->ia_misc_mutex);

   /*
    * Load error counters
    */
   suni_pm7345->suni_id_reset = 0;

   drv_usecwait ((clock_t)1);

   /*
    * FRMR stats
    */
   frmr_st   = ia_get32 (&suni_pm7345->suni_ds3_frm_stat);
   e3_fram_st   = ia_get32 (&suni_pm7345->suni_e3_frm_fram_intr_ind_stat);
   e3_maint_st   = ia_get32 (&suni_pm7345->suni_e3_frm_maint_stat);

   if (softc->suni_carrier) {
      suni_stat->pmon_fbe +=
         (ia_get32 (&suni_pm7345->suni_pmon_fbe_evt_cnt_lsb) & 0xff) +
         ((ia_get32 (&suni_pm7345->suni_pmon_fbe_evt_cnt_msb) & 0x03) << 8);

      suni_stat->pmon_febe +=
         (ia_get32 (&suni_pm7345->suni_pmon_febe_evt_cnt_lsb) & 0xff) +
         ((ia_get32 (&suni_pm7345->suni_pmon_febe_evt_cnt_msb)  & 0x3f) << 8);

      suni_stat->pmon_lcv +=
         (ia_get32 (&suni_pm7345->suni_pmon_lcv_evt_cnt_lsb) & 0xff) +
         ((ia_get32 (&suni_pm7345->suni_pmon_lcv_evt_cnt_msb)  & 0xff) << 8);

      suni_stat->pmon_sez +=
         (ia_get32 (&suni_pm7345->suni_pmon_sez_det_cnt_lsb) & 0xff) +
         ((ia_get32 (&suni_pm7345->suni_pmon_sez_det_cnt_msb)  & 0xff) << 8);

      suni_stat->pmon_perr +=
         (ia_get32 (&suni_pm7345->suni_pmon_pe_evt_cnt_lsb) & 0xff) +
         ((ia_get32 (&suni_pm7345->suni_pmon_pe_evt_cnt_msb)  & 0xff) << 8);

      suni_stat->pmon_pperr +=
         (ia_get32 (&suni_pm7345->suni_pmon_ppe_evt_cnt_lsb) & 0xff) +
         ((ia_get32 (&suni_pm7345->suni_pmon_ppe_evt_cnt_msb)  & 0x3f) << 8);

      suni_stat->cppm_hcse +=
         (ia_get32 (&suni_pm7345->suni_cppm_hcs_err_cnt_lsb) & 0xff) +
         ((ia_get32 (&suni_pm7345->suni_cppm_hcs_err_cnt_msb)  & 0x0f) << 8);

      suni_stat->cppm_rcell +=
         (ia_get32 (&suni_pm7345->suni_cppm_rcv_cell_cnt_lsb) & 0xff) +
         ((ia_get32 (&suni_pm7345->suni_cppm_rcv_cell_cnt_msb)  & 0xff) << 8);

      suni_stat->cppm_tcell +=
         (ia_get32 (&suni_pm7345->suni_cppm_xmit_cell_cnt_lsb) & 0xff) +
         ((ia_get32 (&suni_pm7345->suni_cppm_xmit_cell_cnt_msb)  & 0xff) << 8);
   }

   if (softc->phy_type == PHY_DS3) {
      suni_stat->ds3_frmr_oofv_state = BOL(frmr_st & SUNI_DS3_OOFV);
      suni_stat->ds3_frmr_losv_state = BOL(frmr_st & SUNI_DS3_LOSV);
      suni_stat->ds3_frmr_ferfv_state = BOL(frmr_st & SUNI_DS3_FERFV);
      suni_stat->ds3_frmr_aisv_state = BOL(frmr_st & SUNI_DS3_AISV);
      suni_stat->ds3_frmr_idlv_state = BOL(frmr_st & SUNI_DS3_IDLV);
      suni_stat->ds3_frmr_cbitv_state = BOL(frmr_st & SUNI_DS3_CBITV);
      suni_stat->ds3_frmr_redv_state = BOL(frmr_st & SUNI_DS3_REDV);
   }
   else {
      suni_stat->e3_frmr_oofv_state = BOL(e3_fram_st & SUNI_E3_OOF);
      suni_stat->e3_frmr_losv_state = BOL(e3_fram_st & SUNI_E3_LOS);
      suni_stat->e3_frmr_aisv_state = BOL(e3_maint_st & SUNI_E3_AISD);
      suni_stat->e3_frmr_ferfv_state = BOL(e3_maint_st & SUNI_E3_FERF_RAI);
      suni_stat->e3_frmr_febev_state = BOL(e3_maint_st & SUNI_E3_FEBE);
   }

   /*
    * RXCP interrupt
    */
   rxcp_intr = ia_get32 (&suni_pm7345->suni_rxcp_intr_en_sts);
   rxcp_cs   = ia_get32 (&suni_pm7345->suni_rxcp_ctrl);

   suni_stat->racp_hunt_presync = BOL(rxcp_cs & SUNI_DS3_OOCDV);

   if (rxcp_intr & SUNI_DS3_FUDRI)
      suni_stat->racp_fudr_count++;
   if (rxcp_intr & SUNI_DS3_FOVRI)
      suni_stat->racp_fovr_count++;


   if (softc->suni_carrier) {
      suni_stat->racp_uchcs +=
         (ia_get32 (&suni_pm7345->suni_cppm_hcs_err_cnt_lsb) & 0xff) +
         ((ia_get32 (&suni_pm7345->suni_cppm_hcs_err_cnt_msb) & 0x0f) << 8);
   }

   if (suni_stat->carrier_detect)
      softc->suni_carrier = 1;

#if defined(__SNMP__) || defined(__RLS__)
   if (softc->phy_type == PHY_DS3) {
      if ( (suni_stat->ds3_frmr_oofv_state) ||
            (suni_stat->ds3_frmr_losv_state)) {
         suni_stat->sectionESs++;
         suni_stat->sectionSESs++;
      }

#ifdef   __RLS__
      suni_stat->sectionContiguousCVS = 0;
#endif   /* __RLS__ */

      if (suni_stat->ds3_frmr_oofv_state)
         suni_stat->sectionSEFSs++;

#ifdef   __RLS__
   /*
    * Count the number of contiguous LOS and LOF seconds.
    * If we hit 3, the link has failed. Actually, according to
    * rfc1595, it should be 2.5 +/- 0.5. Since our  granularity is
    * a second, we do it at 3.
    */
      if (suni_stat->ds3_frmr_losv_state)
         suni_stat->sectionContiguousLOSES++;
      else
         suni_stat->sectionContiguousLOSES = 0;

      if (suni_stat->sectionContiguousLOSES >= 3)
         suni_stat->failure = 1;
#endif   /* __RLS__ */

      if (!suni_stat->lineUnAvailableState)
      {
         if (suni_stat->ds3_frmr_aisv_state)
         {
            suni_stat->lineESs++;
            suni_stat->lineSESs++;
            suni_stat->lineContiguousSESs++;
            if (suni_stat->lineContiguousSESs == 10)
            {
               suni_stat->lineUASs += 10;
               suni_stat->lineUnAvailableState = 1;
            }
         }
         else
            suni_stat->lineContiguousSESs = 0;
      }
      else
      {
         if (!suni_stat->ds3_frmr_aisv_state)
            suni_stat->lineContiguousNoSESs++;
         else
         {
            suni_stat->lineUASs +=
               (suni_stat->lineContiguousNoSESs > 0) ?
                  suni_stat->lineContiguousNoSESs : 1;
            suni_stat->lineContiguousNoSESs = 0;

         }
         if (suni_stat->lineContiguousNoSESs == 10)
            suni_stat->lineContiguousNoSESs = suni_stat->lineUnAvailableState = 0;
      }

#ifdef   __RLS__
   /*
    * Check for contiguous coding violations only if
    * is a possibility of such.
    */
      suni_stat->lineContiguousCVS = 0;
#endif   /* __RLS__ */

      if ((suni_stat->pmon_febe - suni_stat->old_pmon_febe) ||
               (suni_stat->ds3_frmr_ferfv_state))
         suni_stat->flESs++;
      if (((suni_stat->pmon_febe - suni_stat->old_pmon_febe) >= 32) ||
               (suni_stat->ds3_frmr_ferfv_state))
         suni_stat->flSESs++;
      suni_stat->flCVs = suni_stat->pmon_febe;

#ifdef   __RLS__
   /*
    * Count the number of contiguous LAIS and FERF seconds.
    * 21 LAIS or 3 FERF indicate a failure, or there has been more
    * than 10 contiguous CV seconds.
    */
      if (suni_stat->ds3_frmr_aisv_state)
         suni_stat->lineContiguousLAISS++;
      else
         suni_stat->lineContiguousLAISS = 0;

      if (suni_stat->ds3_frmr_ferfv_state)
         suni_stat->lineContiguousFERFS++;
      else
         suni_stat->lineContiguousFERFS = 0;

      if ( (suni_stat->lineContiguousLAISS >= 21) ||
            (suni_stat->lineContiguousFERFS >= 3) ||
            (suni_stat->lineContiguousCVS >= 10) )
         suni_stat->failure = 1;
#endif   /* __RLS__ */
   }
   else if (softc->phy_type == PHY_E3) {
      if ( (suni_stat->e3_frmr_oofv_state) ||
            (suni_stat->e3_frmr_losv_state)) {
         suni_stat->sectionESs++;
         suni_stat->sectionSESs++;
      }

#ifdef   __RLS__
      suni_stat->sectionContiguousCVS = 0;
#endif   /* __RLS__ */

      if (suni_stat->e3_frmr_oofv_state)
         suni_stat->sectionSEFSs++;

#ifdef   __RLS__
   /*
    * Count the number of contiguous LOS and LOF seconds.
    * If we hit 3, the link has failed. Actually, according to
    * rfc1595, it should be 2.5 +/- 0.5. Since our  granularity is
    * a second, we do it at 3.
    */
      if (suni_stat->e3_frmr_losv_state)
         suni_stat->sectionContiguousLOSES++;
      else
         suni_stat->sectionContiguousLOSES = 0;

      if (suni_stat->sectionContiguousLOSES >= 3)
         suni_stat->failure = 1;
#endif   /* __RLS__ */

      if (!suni_stat->lineUnAvailableState)
      {
         if (suni_stat->e3_frmr_aisv_state)
         {
            suni_stat->lineESs++;
            suni_stat->lineSESs++;
            suni_stat->lineContiguousSESs++;
            if (suni_stat->lineContiguousSESs == 10)
            {
               suni_stat->lineUASs += 10;
               suni_stat->lineUnAvailableState = 1;
            }
         }
         else
            suni_stat->lineContiguousSESs = 0;
      }
      else
      {
         if (!suni_stat->e3_frmr_aisv_state)
            suni_stat->lineContiguousNoSESs++;
         else
         {
            suni_stat->lineUASs +=
               (suni_stat->lineContiguousNoSESs > 0) ?
                  suni_stat->lineContiguousNoSESs : 1;
            suni_stat->lineContiguousNoSESs = 0;

         }
         if (suni_stat->lineContiguousNoSESs == 10)
            suni_stat->lineContiguousNoSESs = suni_stat->lineUnAvailableState = 0;
      }

#ifdef   __RLS__
   /*
    * Check for contiguous coding violations only if
    * is a possibility of such.
    */
      suni_stat->lineContiguousCVS = 0;
#endif   /* __RLS__ */

      if ((suni_stat->pmon_febe - suni_stat->old_pmon_febe) ||
               (suni_stat->e3_frmr_ferfv_state))
         suni_stat->flESs++;
      if (((suni_stat->pmon_febe - suni_stat->old_pmon_febe) >= 32) ||
               (suni_stat->e3_frmr_ferfv_state))
         suni_stat->flSESs++;
      suni_stat->flCVs = suni_stat->pmon_febe;

#ifdef   __RLS__
   /*
    * Count the number of contiguous LAIS and FERF seconds.
    * 21 LAIS or 3 FERF indicate a failure, or there has been more
    * than 10 contiguous CV seconds.
    */
      if (suni_stat->e3_frmr_aisv_state)
         suni_stat->lineContiguousLAISS++;
      else
         suni_stat->lineContiguousLAISS = 0;

      if (suni_stat->e3_frmr_ferfv_state)
         suni_stat->lineContiguousFERFS++;
      else
         suni_stat->lineContiguousFERFS = 0;

      if ( (suni_stat->lineContiguousLAISS >= 21) ||
            (suni_stat->lineContiguousFERFS >= 3) ||
            (suni_stat->lineContiguousCVS >= 10) )
         suni_stat->failure = 1;
#endif   /* __RLS__ */
   }

   suni_stat->old_pmon_febe = suni_stat->pmon_febe;

   if (suni_stat->failure) {
      suni_stat->failure = 0;
      ia_rollover (softc, 0, 0); /* Link Down */
   }

   if (  (suni_stat->sectionContiguousLOSES) ||
      (suni_stat->sectionContiguousCVS) ||
      (suni_stat->lineContiguousLAISS) ||
      (suni_stat->lineContiguousFERFS) ||
      (suni_stat->lineContiguousCVS)
   )
      suni_stat->no_failure_secs = 0;
   else
      suni_stat->no_failure_secs++;

   if (suni_stat->no_failure_secs >= 10)
      ia_rollover (softc, 1, 0); /* Link Up */

#endif /* __SNMP__ || __RLS__ */

   mutex_exit (&softc->ia_misc_mutex);

   softc->suni_timeout =
      timeout (ia_suni_pm7345_timer, (caddr_t)softc, drv_usectohz(1000000));
}

/*F
 * FUNCTION: ia_suni_framing
 *
 * DESCRIPTION:
 *      Set SUNI to Sonet or SDH mode.
 *
 * ARGUMENTS:
 *      softc           Device state structure
 *
 * ENVIRONMENT:
 *      Called from ia_ioctl
 *
 * RETURN:
 *      Nothing
 *
 * NOTES:
 *
 */
void
ia_suni_framing (ia_softc_t *softc, int mode)
{
        Vol ia_suni_t   *suni = softc->brd_regs.suni;

        if (mode) {
                suni->suni_tpop_arb_prtm |= 0x08;       /* S[1] = 1 */
                suni->suni_tacp_idle_hdr_pat = 1;
        }
        else {
                suni->suni_tpop_arb_prtm &= ~0x08;      /* S[1] = 1 */
                suni->suni_tacp_idle_hdr_pat = 0;
        }
}
#endif
