/*- * Copyright (c) 2003 * Fraunhofer Institute for Open Communication Systems (FhG Fokus). * All rights reserved. * * Author: Hartmut Brandt * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * set SONET/SDH mode */ static int suni_set_sdh(struct utopia *utp, int sdh) { int err; if (sdh) err = UTP_WRITEREG(utp, SUNI_REGO_TPOPAPTR + 1, SUNI_REGM_TPOPAPTR_S, SUNI_REGM_SDH << SUNI_REGS_TPOPAPTR_S); else err = UTP_WRITEREG(utp, SUNI_REGO_TPOPAPTR + 1, SUNI_REGM_TPOPAPTR_S, SUNI_REGM_SONET << SUNI_REGS_TPOPAPTR_S); if (err != 0) return (err); utp->state &= ~UTP_ST_SDH; if (sdh) utp->state |= UTP_ST_SDH; return (0); } /* * set idle/unassigned cells */ static int suni_set_unass(struct utopia *utp, int unass) { int err; if (unass) err = UTP_WRITEREG(utp, SUNI_REGO_TACPIDLEH, 0xff, (0 << SUNI_REGS_TACPIDLEH_CLP)); else err = UTP_WRITEREG(utp, SUNI_REGO_TACPIDLEH, 0xff, (1 << SUNI_REGS_TACPIDLEH_CLP)); if (err != 0) return (err); utp->state &= ~UTP_ST_UNASS; if (unass) utp->state |= UTP_ST_UNASS; return (0); } /* * enable/disable scrambling */ static int suni_set_noscramb(struct utopia *utp, int noscramb) { int err; if (noscramb) { err = UTP_WRITEREG(utp, SUNI_REGO_TACPCTRL, SUNI_REGM_TACPCTRL_DSCR, SUNI_REGM_TACPCTRL_DSCR); if (err) return (err); err = UTP_WRITEREG(utp, SUNI_REGO_RACPCTRL, SUNI_REGM_RACPCTRL_DDSCR, SUNI_REGM_RACPCTRL_DDSCR); if (err) return (err); utp->state |= UTP_ST_NOSCRAMB; } else { err = UTP_WRITEREG(utp, SUNI_REGO_TACPCTRL, SUNI_REGM_TACPCTRL_DSCR, 0); if (err) return (err); err = UTP_WRITEREG(utp, SUNI_REGO_RACPCTRL, SUNI_REGM_RACPCTRL_DDSCR, 0); if (err) return (err); utp->state &= ~UTP_ST_NOSCRAMB; } return (0); } /* * Get current carrier state */ static int suni_update_carrier(struct utopia *utp) { int err; uint8_t reg; u_int n = 1; if ((err = UTP_READREGS(utp, SUNI_REGO_RSOPSIS, ®, &n)) != 0) { utp->carrier = UTP_CARR_UNKNOWN; return (err); } utopia_check_carrier(utp, !(reg & SUNI_REGM_RSOPSIS_LOSV)); return (0); } /* * Set the SUNI chip to reflect the current state in utopia. * Assume, that the chip has been reset. */ static int suni_set_chip(struct utopia *utp) { int err = 0; /* set sonet/sdh */ err |= utopia_set_sdh(utp, utp->state & UTP_ST_SDH); /* unassigned or idle cells */ err |= utopia_set_unass(utp, utp->state & UTP_ST_UNASS); err |= UTP_WRITEREG(utp, SUNI_REGO_TACPIDLEP, 0xff, 0x6a); /* set scrambling */ err |= utopia_set_noscramb(utp, utp->state & UTP_ST_NOSCRAMB); /* loopback */ err |= utopia_set_loopback(utp, utp->loopback); /* update carrier state */ err |= utopia_update_carrier(utp); /* enable interrupts on LOS */ err |= UTP_WRITEREG(utp, SUNI_REGO_RSOPCIE, SUNI_REGM_RSOPCIE_LOSE, SUNI_REGM_RSOPCIE_LOSE); return (err ? EIO : 0); } /* * Reset the SUNI chip to reflect the current state of utopia. */ static int suni_reset_default(struct utopia *utp) { int err = 0; if (!(utp->flags & UTP_FL_NORESET)) { err |= UTP_WRITEREG(utp, SUNI_REGO_MRESET, SUNI_REGM_MRESET_RESET, SUNI_REGM_MRESET_RESET); err |= UTP_WRITEREG(utp, SUNI_REGO_MRESET, SUNI_REGM_MRESET_RESET, 0); } /* disable test mode */ err |= UTP_WRITEREG(utp, SUNI_REGO_MTEST, 0xff, 0x00); err |= suni_set_chip(utp); return (err ? EIO : 0); } /* * Set loopback mode for the Lite */ static int suni_set_loopback_lite(struct utopia *utp, u_int mode) { int err; uint32_t val; u_int nmode; val = 0; nmode = mode; if (mode & UTP_LOOP_TIME) { nmode &= ~UTP_LOOP_TIME; val |= SUNI_REGM_MCTRL_LOOPT; } if (mode & UTP_LOOP_DIAG) { nmode &= ~UTP_LOOP_DIAG; val |= SUNI_REGM_MCTRL_DLE; } if (mode & UTP_LOOP_LINE) { nmode &= ~UTP_LOOP_LINE; if (val & SUNI_REGM_MCTRL_DLE) return (EINVAL); val |= SUNI_REGM_MCTRL_LLE; } if (nmode != 0) return (EINVAL); err = UTP_WRITEREG(utp, SUNI_REGO_MCTRL, SUNI_REGM_MCTRL_LLE | SUNI_REGM_MCTRL_DLE | SUNI_REGM_MCTRL_LOOPT, val); if (err) return (err); utp->loopback = mode; return (0); } /* * Update statistics from a SUNI/LITE or SUNI/ULTRA */ static void suni_lite_update_stats(struct utopia *utp) { int err; /* write to the master if we can */ if (!(utp->flags & UTP_FL_NORESET)) { err = UTP_WRITEREG(utp, SUNI_REGO_MRESET, 0, 0); } else { err = UTP_WRITEREG(utp, SUNI_REGO_RSOP_BIP8, 0, 0); err |= UTP_WRITEREG(utp, SUNI_REGO_RLOPBIP8_24, 0, 0); err |= UTP_WRITEREG(utp, SUNI_REGO_RPOPBIP8, 0, 0); err |= UTP_WRITEREG(utp, SUNI_REGO_RACPCHCS, 0, 0); err |= UTP_WRITEREG(utp, SUNI_REGO_TACPCNT, 0, 0); } if (err) { #ifdef DIAGNOSTIC printf("%s: register write error %s: %d\n", __func__, utp->chip->name, err); #endif return; } DELAY(8); utp->stats.rx_sbip += utopia_update(utp, SUNI_REGO_RSOP_BIP8, 2, 0xffff); utp->stats.rx_lbip += utopia_update(utp, SUNI_REGO_RLOPBIP8_24, 3, 0xfffff); utp->stats.rx_lfebe += utopia_update(utp, SUNI_REGO_RLOPFEBE, 3, 0xfffff); utp->stats.rx_pbip += utopia_update(utp, SUNI_REGO_RPOPBIP8, 2, 0xffff); utp->stats.rx_pfebe += utopia_update(utp, SUNI_REGO_RPOPFEBE, 2, 0xffff); utp->stats.rx_corr += utopia_update(utp, SUNI_REGO_RACPCHCS, 1, 0xff); utp->stats.rx_uncorr += utopia_update(utp, SUNI_REGO_RACPUHCS, 1, 0xff); utp->stats.rx_cells += utopia_update(utp, SUNI_REGO_RACPCNT, 3, 0x7ffff); utp->stats.tx_cells += utopia_update(utp, SUNI_REGO_TACPCNT, 3, 0x7ffff); } /* * Handle interrupt on SUNI chip */ static void suni_intr_default(struct utopia *utp) { uint8_t regs[SUNI_REGO_MTEST]; u_int n = SUNI_REGO_MTEST; int err; /* Read all registers. This acks the interrupts */ if ((err = UTP_READREGS(utp, SUNI_REGO_MRESET, regs, &n)) != 0) { printf("SUNI read error %d\n", err); return; } if (n <= SUNI_REGO_RSOPSIS) { printf("%s: could not read RSOPSIS", __func__); return; } /* check for LOSI (loss of signal) */ if ((regs[SUNI_REGO_MISTATUS] & SUNI_REGM_MISTATUS_RSOPI) && (regs[SUNI_REGO_RSOPSIS] & SUNI_REGM_RSOPSIS_LOSI)) utopia_check_carrier(utp, !(regs[SUNI_REGO_RSOPSIS] & SUNI_REGM_RSOPSIS_LOSV)); } const struct utopia_chip utopia_chip_lite = { UTP_TYPE_SUNI_LITE, "Suni/Lite (PMC-5346)", 256, suni_reset_default, suni_set_sdh, suni_set_unass, suni_set_noscramb, suni_update_carrier, suni_set_loopback_lite, suni_intr_default, suni_lite_update_stats, }; /* * Set loopback mode for the Ultra */ static int suni_set_loopback_ultra(struct utopia *utp, u_int mode) { int err; uint32_t val; u_int nmode; val = 0; nmode = mode; if (mode & UTP_LOOP_TIME) { nmode &= ~UTP_LOOP_TIME; val |= SUNI_REGM_MCTRL_LOOPT; } if (mode & UTP_LOOP_DIAG) { nmode &= ~UTP_LOOP_DIAG; if (val & SUNI_REGM_MCTRL_LOOPT) return (EINVAL); val |= SUNI_REGM_MCTRL_SDLE; } if (mode & UTP_LOOP_LINE) { nmode &= ~UTP_LOOP_LINE; if (val & (SUNI_REGM_MCTRL_LOOPT | SUNI_REGM_MCTRL_SDLE)) return (EINVAL); val |= SUNI_REGM_MCTRL_LLE; } if (mode & UTP_LOOP_PARAL) { nmode &= ~UTP_LOOP_PARAL; val |= SUNI_REGM_MCTRL_PDLE; } if (mode & UTP_LOOP_TWIST) { nmode &= ~UTP_LOOP_TWIST; val |= SUNI_REGM_MCTRL_TPLE; } if (nmode != 0) return (EINVAL); err = UTP_WRITEREG(utp, SUNI_REGO_MCTRL, SUNI_REGM_MCTRL_LLE | SUNI_REGM_MCTRL_SDLE | SUNI_REGM_MCTRL_LOOPT | SUNI_REGM_MCTRL_PDLE | SUNI_REGM_MCTRL_TPLE, val); if (err) return (err); utp->loopback = mode; return (0); } const struct utopia_chip utopia_chip_ultra = { UTP_TYPE_SUNI_ULTRA, "Suni/Ultra (PMC-5350)", 256, suni_reset_default, suni_set_sdh, suni_set_unass, suni_set_noscramb, suni_update_carrier, suni_set_loopback_ultra, suni_intr_default, suni_lite_update_stats, }; /* * Set loopback mode for the 622 */ static int suni_set_loopback_622(struct utopia *utp, u_int mode) { int err; uint32_t val; uint8_t config; int smode; u_int nmode; u_int n = 1; val = 0; nmode = mode; if (mode & UTP_LOOP_PATH) { nmode &= ~UTP_LOOP_PATH; val |= SUNI_REGM_MCTRLM_DPLE; } err = UTP_READREGS(utp, SUNI_REGO_MCONFIG, &config, &n); if (err != 0) return (err); smode = ((config & SUNI_REGM_MCONFIG_TMODE_622) == SUNI_REGM_MCONFIG_TMODE_STS1_BIT && (config & SUNI_REGM_MCONFIG_RMODE_622) == SUNI_REGM_MCONFIG_RMODE_STS1_BIT); if (mode & UTP_LOOP_TIME) { if (!smode) return (EINVAL); nmode &= ~UTP_LOOP_TIME; val |= SUNI_REGM_MCTRLM_LOOPT; } if (mode & UTP_LOOP_DIAG) { nmode &= ~UTP_LOOP_DIAG; if (val & SUNI_REGM_MCTRLM_LOOPT) return (EINVAL); val |= SUNI_REGM_MCTRLM_DLE; } if (mode & UTP_LOOP_LINE) { nmode &= ~UTP_LOOP_LINE; if (val & (SUNI_REGM_MCTRLM_LOOPT | SUNI_REGM_MCTRLM_DLE)) return (EINVAL); val |= SUNI_REGM_MCTRLM_LLE; } if (nmode != 0) return (EINVAL); err = UTP_WRITEREG(utp, SUNI_REGO_MCTRLM, SUNI_REGM_MCTRLM_LLE | SUNI_REGM_MCTRLM_DLE | SUNI_REGM_MCTRLM_DPLE | SUNI_REGM_MCTRL_LOOPT, val); if (err) return (err); utp->loopback = mode; return (0); } /* * Reset the SUNI chip to reflect the current state of utopia. */ static int suni_reset_622(struct utopia *utp) { int err = 0; if (!(utp->flags & UTP_FL_NORESET)) { err |= UTP_WRITEREG(utp, SUNI_REGO_MRESET, SUNI_REGM_MRESET_RESET, SUNI_REGM_MRESET_RESET); err |= UTP_WRITEREG(utp, SUNI_REGO_MRESET, SUNI_REGM_MRESET_RESET, 0); } /* disable test mode */ err |= UTP_WRITEREG(utp, SUNI_REGO_MTEST, 0xff, SUNI_REGM_MTEST_DS27_53_622); err |= suni_set_chip(utp); return (err ? EIO : 0); } /* * Update statistics from a SUNI/622 */ static void suni_622_update_stats(struct utopia *utp) { int err; /* write to the master if we can */ if (!(utp->flags & UTP_FL_NORESET)) { err = UTP_WRITEREG(utp, SUNI_REGO_MRESET, 0, 0); } else { err = UTP_WRITEREG(utp, SUNI_REGO_RSOP_BIP8, 0, 0); err |= UTP_WRITEREG(utp, SUNI_REGO_RLOPBIP8_24, 0, 0); err |= UTP_WRITEREG(utp, SUNI_REGO_RPOPBIP8, 0, 0); err |= UTP_WRITEREG(utp, SUNI_REGO_RACPCHCS, 0, 0); err |= UTP_WRITEREG(utp, SUNI_REGO_TACPCNT, 0, 0); } if (err) { #ifdef DIAGNOSTIC printf("%s: register write error %s: %d\n", __func__, utp->chip->name, err); #endif return; } DELAY(8); utp->stats.rx_sbip += utopia_update(utp, SUNI_REGO_RSOP_BIP8, 2, 0xffff); utp->stats.rx_lbip += utopia_update(utp, SUNI_REGO_RLOPBIP8_24, 3, 0xfffff); utp->stats.rx_lfebe += utopia_update(utp, SUNI_REGO_RLOPFEBE, 3, 0xfffff); utp->stats.rx_pbip += utopia_update(utp, SUNI_REGO_RPOPBIP8, 2, 0xffff); utp->stats.rx_pfebe += utopia_update(utp, SUNI_REGO_RPOPFEBE, 2, 0xffff); utp->stats.rx_corr += utopia_update(utp, SUNI_REGO_RACPCHCS_622, 2, 0xfff); utp->stats.rx_uncorr += utopia_update(utp, SUNI_REGO_RACPUHCS_622, 2, 0xfff); utp->stats.rx_cells += utopia_update(utp, SUNI_REGO_RACPCNT_622, 3, 0x1fffff); utp->stats.tx_cells += utopia_update(utp, SUNI_REGO_TACPCNT, 3, 0x1fffff); } const struct utopia_chip utopia_chip_622 = { UTP_TYPE_SUNI_622, "Suni/622 (PMC-5355)", 256, suni_reset_622, suni_set_sdh, suni_set_unass, suni_set_noscramb, suni_update_carrier, suni_set_loopback_622, suni_intr_default, suni_622_update_stats, };