summaryrefslogtreecommitdiffstats
path: root/sys/dev/utopia/utopia.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/utopia/utopia.c')
-rw-r--r--sys/dev/utopia/utopia.c1082
1 files changed, 1082 insertions, 0 deletions
diff --git a/sys/dev/utopia/utopia.c b/sys/dev/utopia/utopia.c
new file mode 100644
index 0000000..ce63186
--- /dev/null
+++ b/sys/dev/utopia/utopia.c
@@ -0,0 +1,1082 @@
+/*
+ * Copyright (c) 2003
+ * Fraunhofer Institute for Open Communication Systems (FhG Fokus).
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * Author: Hartmut Brandt <harti@freebsd.org>
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/unistd.h>
+#include <sys/kernel.h>
+#include <sys/kthread.h>
+#include <sys/proc.h>
+#include <sys/bus.h>
+#include <sys/malloc.h>
+#include <sys/sysctl.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/socket.h>
+
+#include <net/if.h>
+#include <net/if_var.h>
+#include <net/if_media.h>
+#include <net/if_atm.h>
+
+#include <dev/utopia/suni.h>
+#include <dev/utopia/idtphy.h>
+#include <dev/utopia/utopia.h>
+
+#define READREGS(UTOPIA, REG, VALP, NP) \
+ (UTOPIA)->methods->readregs((UTOPIA)->ifatm, REG, VALP, NP)
+#define WRITEREG(UTOPIA, REG, MASK, VAL) \
+ (UTOPIA)->methods->writereg((UTOPIA)->ifatm, REG, MASK, VAL)
+
+/*
+ * Global list of all registered interfaces
+ */
+static struct mtx utopia_list_mtx;
+static LIST_HEAD(, utopia) utopia_list = LIST_HEAD_INITIALIZER(utopia_list);
+
+#define UTP_RLOCK_LIST() mtx_lock(&utopia_list_mtx)
+#define UTP_RUNLOCK_LIST() mtx_unlock(&utopia_list_mtx)
+#define UTP_WLOCK_LIST() mtx_lock(&utopia_list_mtx)
+#define UTP_WUNLOCK_LIST() mtx_unlock(&utopia_list_mtx)
+
+#define UTP_LOCK(UTP) mtx_lock((UTP)->lock)
+#define UTP_UNLOCK(UTP) mtx_unlock((UTP)->lock)
+#define UTP_LOCK_ASSERT(UTP) mtx_assert((UTP)->lock, MA_OWNED)
+
+static struct proc *utopia_kproc;
+
+static void utopia_dump(struct utopia *) __unused;
+
+/*
+ * Debugging - dump all registers.
+ */
+static void
+utopia_dump(struct utopia *utp)
+{
+ uint8_t regs[256];
+ u_int n = 256, i;
+ int err;
+
+ if ((err = READREGS(utp, SUNI_REGO_MRESET, regs, &n)) != 0) {
+ printf("SUNI read error %d\n", err);
+ return;
+ }
+ for (i = 0; i < n; i++) {
+ if (i % 16 == 0)
+ printf("%02x:", i);
+ if (i % 16 == 8)
+ printf(" ");
+ printf(" %02x", regs[i]);
+ if (i % 16 == 15)
+ printf("\n");
+ }
+ if (i % 16 != 0)
+ printf("\n");
+}
+
+/*
+ * Update the carrier status
+ */
+static void
+utopia_check_carrier(struct utopia *utp, u_int carr_ok)
+{
+ int old;
+
+ old = utp->carrier;
+ if (carr_ok) {
+ /* carrier */
+ utp->carrier = UTP_CARR_OK;
+ if (old != UTP_CARR_OK) {
+ if_printf(&utp->ifatm->ifnet, "carrier detected\n");
+ }
+ } else {
+ /* no carrier */
+ utp->carrier = UTP_CARR_LOST;
+ if (old == UTP_CARR_OK) {
+ if_printf(&utp->ifatm->ifnet, "carrier lost\n");
+ }
+ }
+}
+
+static int
+utopia_update_carrier_default(struct utopia *utp)
+{
+ int err;
+ uint8_t reg;
+ u_int n = 1;
+
+ if ((err = READREGS(utp, SUNI_REGO_RSOPSIS, &reg, &n)) != 0) {
+ utp->carrier = UTP_CARR_UNKNOWN;
+ return (err);
+ }
+ utopia_check_carrier(utp, !(reg & SUNI_REGM_RSOPSIS_LOSV));
+ return (0);
+}
+
+/*
+ * enable/disable scrambling
+ */
+static int
+utopia_set_noscramb_default(struct utopia *utp, int noscramb)
+{
+ int err;
+
+ if (noscramb) {
+ err = WRITEREG(utp, SUNI_REGO_TACPCTRL,
+ SUNI_REGM_TACPCTRL_DSCR, SUNI_REGM_TACPCTRL_DSCR);
+ if (err)
+ return (err);
+ err = WRITEREG(utp, SUNI_REGO_RACPCTRL,
+ SUNI_REGM_RACPCTRL_DDSCR, SUNI_REGM_RACPCTRL_DDSCR);
+ if (err)
+ return (err);
+ utp->state |= UTP_ST_NOSCRAMB;
+ } else {
+ err = WRITEREG(utp, SUNI_REGO_TACPCTRL,
+ SUNI_REGM_TACPCTRL_DSCR, 0);
+ if (err)
+ return (err);
+ err = WRITEREG(utp, SUNI_REGO_RACPCTRL,
+ SUNI_REGM_RACPCTRL_DDSCR, 0);
+ if (err)
+ return (err);
+ utp->state &= ~UTP_ST_NOSCRAMB;
+ }
+ return (0);
+}
+
+/*
+ * set SONET/SDH mode
+ */
+static int
+utopia_set_sdh_default(struct utopia *utp, int sdh)
+{
+ int err;
+
+ if (sdh)
+ err = WRITEREG(utp, SUNI_REGO_TPOPAPTR + 1,
+ SUNI_REGM_TPOPAPTR_S,
+ SUNI_REGM_SDH << SUNI_REGS_TPOPAPTR_S);
+ else
+ err = 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
+utopia_set_unass_default(struct utopia *utp, int unass)
+{
+ int err;
+
+ if (unass)
+ err = WRITEREG(utp, SUNI_REGO_TACPIDLEH,
+ 0xff, (0 << SUNI_REGS_TACPIDLEH_CLP));
+ else
+ err = 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);
+}
+
+/*
+ * Set loopback mode for the Lite
+ */
+static int
+utopia_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 = 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);
+}
+
+/*
+ * Set loopback mode for the Ultra
+ */
+static int
+utopia_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 = 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);
+}
+
+/*
+ * Set loopback mode for the Ultra
+ */
+static int
+utopia_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 = 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 = 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);
+}
+
+/*
+ * Set the SUNI chip to reflect the current state in utopia.
+ * Assume, that the chip has been reset.
+ */
+static int
+utopia_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 |= WRITEREG(utp, SUNI_REGO_TACPIDLEP, 0xff, 0x6a);
+
+ /* loopback */
+ err |= utopia_set_loopback(utp, utp->loopback);
+
+ /* update carrier state */
+ err |= utopia_update_carrier(utp);
+
+ /* enable interrupts on LOS */
+ err |= 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
+utopia_reset_default(struct utopia *utp)
+{
+ int err = 0;
+
+ if (!(utp->flags & UTP_FL_NORESET)) {
+ err |= WRITEREG(utp, SUNI_REGO_MRESET, SUNI_REGM_MRESET_RESET,
+ SUNI_REGM_MRESET_RESET);
+ err |= WRITEREG(utp, SUNI_REGO_MRESET, SUNI_REGM_MRESET_RESET,
+ 0);
+ }
+
+ /* disable test mode */
+ err |= WRITEREG(utp, SUNI_REGO_MTEST, 0xff, 0x00);
+
+ err |= utopia_set_chip(utp);
+
+ return (err ? EIO : 0);
+}
+
+/*
+ * Reset the SUNI chip to reflect the current state of utopia.
+ */
+static int
+utopia_reset_622(struct utopia *utp)
+{
+ int err = 0;
+
+ if (!(utp->flags & UTP_FL_NORESET)) {
+ err |= WRITEREG(utp, SUNI_REGO_MRESET, SUNI_REGM_MRESET_RESET,
+ SUNI_REGM_MRESET_RESET);
+ err |= WRITEREG(utp, SUNI_REGO_MRESET, SUNI_REGM_MRESET_RESET,
+ 0);
+ }
+
+ /* disable test mode */
+ err |= WRITEREG(utp, SUNI_REGO_MTEST, 0xff,
+ SUNI_REGM_MTEST_DS27_53_622);
+
+ err |= utopia_set_chip(utp);
+
+ return (err ? EIO : 0);
+}
+
+/*
+ * Handle interrupt on lite chip
+ */
+static void
+utopia_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 = 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));
+}
+
+static const struct utopia_chip chip_622 = {
+ UTP_TYPE_SUNI_622,
+ "Suni/622 (PMC-5355)",
+ 256,
+ utopia_reset_622,
+ utopia_set_sdh_default,
+ utopia_set_unass_default,
+ utopia_set_noscramb_default,
+ utopia_update_carrier_default,
+ utopia_set_loopback_622,
+ utopia_intr_default,
+};
+static const struct utopia_chip chip_lite = {
+ UTP_TYPE_SUNI_LITE,
+ "Suni/Lite (PMC-5346)",
+ 256,
+ utopia_reset_default,
+ utopia_set_sdh_default,
+ utopia_set_unass_default,
+ utopia_set_noscramb_default,
+ utopia_update_carrier_default,
+ utopia_set_loopback_lite,
+ utopia_intr_default,
+};
+static const struct utopia_chip chip_ultra = {
+ UTP_TYPE_SUNI_ULTRA,
+ "Suni/Ultra (PMC-5350)",
+ 256,
+ utopia_reset_default,
+ utopia_set_sdh_default,
+ utopia_set_unass_default,
+ utopia_set_noscramb_default,
+ utopia_update_carrier_default,
+ utopia_set_loopback_ultra,
+ utopia_intr_default,
+};
+
+/*
+ * Reset IDT77105. There is really no way to reset this thing by acessing
+ * the registers. Load the registers with default values.
+ */
+static int
+idt77105_reset(struct utopia *utp)
+{
+ int err = 0;
+ u_int n;
+ uint8_t val[2];
+
+ err |= WRITEREG(utp, IDTPHY_REGO_MCR, 0xff,
+ IDTPHY_REGM_MCR_DRIC | IDTPHY_REGM_MCR_EI);
+ n = 1;
+ err |= READREGS(utp, IDTPHY_REGO_ISTAT, val, &n);
+ err |= WRITEREG(utp, IDTPHY_REGO_DIAG, 0xff, 0);
+ err |= WRITEREG(utp, IDTPHY_REGO_LHEC, 0xff, 0);
+
+ err |= WRITEREG(utp, IDTPHY_REGO_CNTS, 0xff, IDTPHY_REGM_CNTS_SEC);
+ n = 2;
+ err |= READREGS(utp, IDTPHY_REGO_CNT, val, &n);
+
+ err |= WRITEREG(utp, IDTPHY_REGO_CNTS, 0xff, IDTPHY_REGM_CNTS_TX);
+ n = 2;
+ err |= READREGS(utp, IDTPHY_REGO_CNT, val, &n);
+
+ err |= WRITEREG(utp, IDTPHY_REGO_CNTS, 0xff, IDTPHY_REGM_CNTS_RX);
+ n = 2;
+ err |= READREGS(utp, IDTPHY_REGO_CNT, val, &n);
+
+ err |= WRITEREG(utp, IDTPHY_REGO_CNTS, 0xff, IDTPHY_REGM_CNTS_HECE);
+ n = 2;
+ err |= READREGS(utp, IDTPHY_REGO_CNT, val, &n);
+
+ err |= WRITEREG(utp, IDTPHY_REGO_MCR, IDTPHY_REGM_MCR_DREC,
+ IDTPHY_REGM_MCR_DREC);
+ err |= WRITEREG(utp, IDTPHY_REGO_DIAG, IDTPHY_REGM_DIAG_RFLUSH,
+ IDTPHY_REGM_DIAG_RFLUSH);
+
+ /* loopback */
+ err |= utopia_set_loopback(utp, utp->loopback);
+
+ /* update carrier state */
+ err |= utopia_update_carrier(utp);
+
+ return (err ? EIO : 0);
+}
+
+static int
+unknown_inval(struct utopia *utp, int what __unused)
+{
+ return (EINVAL);
+}
+
+static int
+idt77105_update_carrier(struct utopia *utp)
+{
+ int err;
+ uint8_t reg;
+ u_int n = 1;
+
+ if ((err = READREGS(utp, IDTPHY_REGO_ISTAT, &reg, &n)) != 0) {
+ utp->carrier = UTP_CARR_UNKNOWN;
+ return (err);
+ }
+ utopia_check_carrier(utp, reg & IDTPHY_REGM_ISTAT_GOOD);
+ return (0);
+}
+
+static int
+idt77105_set_loopback(struct utopia *utp, u_int mode)
+{
+ int err;
+
+ switch (mode) {
+ case UTP_LOOP_NONE:
+ err = WRITEREG(utp, IDTPHY_REGO_DIAG,
+ IDTPHY_REGM_DIAG_LOOP, IDTPHY_REGM_DIAG_LOOP_NONE);
+ break;
+
+ case UTP_LOOP_DIAG:
+ err = WRITEREG(utp, IDTPHY_REGO_DIAG,
+ IDTPHY_REGM_DIAG_LOOP, IDTPHY_REGM_DIAG_LOOP_PHY);
+ break;
+
+ case UTP_LOOP_LINE:
+ err = WRITEREG(utp, IDTPHY_REGO_DIAG,
+ IDTPHY_REGM_DIAG_LOOP, IDTPHY_REGM_DIAG_LOOP_LINE);
+ break;
+
+ default:
+ return (EINVAL);
+ }
+ if (err)
+ return (err);
+ utp->loopback = mode;
+ return (0);
+}
+
+/*
+ * Handle interrupt on IDT77105 chip
+ */
+static void
+idt77105_intr(struct utopia *utp)
+{
+ uint8_t reg;
+ u_int n = 1;
+ int err;
+
+ /* Interrupt status and ack the interrupt */
+ if ((err = READREGS(utp, IDTPHY_REGO_ISTAT, &reg, &n)) != 0) {
+ printf("IDT77105 read error %d\n", err);
+ return;
+ }
+ /* check for signal condition */
+ utopia_check_carrier(utp, reg & IDTPHY_REGM_ISTAT_GOOD);
+}
+
+static const struct utopia_chip chip_idt77105 = {
+ UTP_TYPE_IDT77105,
+ "IDT77105",
+ 7,
+ idt77105_reset,
+ unknown_inval,
+ unknown_inval,
+ unknown_inval,
+ idt77105_update_carrier,
+ idt77105_set_loopback,
+ idt77105_intr,
+};
+
+static int
+unknown_reset(struct utopia *utp __unused)
+{
+ return (EIO);
+}
+
+static int
+unknown_update_carrier(struct utopia *utp)
+{
+ utp->carrier = UTP_CARR_UNKNOWN;
+ return (0);
+}
+
+static int
+unknown_set_loopback(struct utopia *utp __unused, u_int mode __unused)
+{
+ return (EINVAL);
+}
+
+static void
+unknown_intr(struct utopia *utp __unused)
+{
+}
+
+static const struct utopia_chip chip_unknown = {
+ UTP_TYPE_UNKNOWN,
+ "unknown",
+ 0,
+ unknown_reset,
+ unknown_inval,
+ unknown_inval,
+ unknown_inval,
+ unknown_update_carrier,
+ unknown_set_loopback,
+ unknown_intr,
+};
+
+/*
+ * Callbacks for the ifmedia infrastructure.
+ */
+static int
+utopia_media_change(struct ifnet *ifp)
+{
+ struct ifatm *ifatm = (struct ifatm *)ifp->if_softc;
+ struct utopia *utp = ifatm->phy;
+ int error = 0;
+
+ UTP_LOCK(utp);
+ if (utp->chip->type != UTP_TYPE_UNKNOWN && utp->state & UTP_ST_ACTIVE) {
+ if (utp->media->ifm_media & IFM_ATM_SDH) {
+ if (!(utp->state & UTP_ST_SDH))
+ error = utopia_set_sdh(utp, 1);
+ } else {
+ if (utp->state & UTP_ST_SDH)
+ error = utopia_set_sdh(utp, 0);
+ }
+ if (utp->media->ifm_media & IFM_ATM_UNASSIGNED) {
+ if (!(utp->state & UTP_ST_UNASS))
+ error = utopia_set_unass(utp, 1);
+ } else {
+ if (utp->state & UTP_ST_UNASS)
+ error = utopia_set_unass(utp, 0);
+ }
+ if (utp->media->ifm_media & IFM_ATM_NOSCRAMB) {
+ if (!(utp->state & UTP_ST_NOSCRAMB))
+ error = utopia_set_noscramb(utp, 1);
+ } else {
+ if (utp->state & UTP_ST_NOSCRAMB)
+ error = utopia_set_noscramb(utp, 0);
+ }
+ } else
+ error = EIO;
+ UTP_UNLOCK(utp);
+ return (error);
+}
+
+/*
+ * Look at the carrier status.
+ */
+static void
+utopia_media_status(struct ifnet *ifp, struct ifmediareq *ifmr)
+{
+ struct utopia *utp = ((struct ifatm *)ifp->if_softc)->phy;
+
+ UTP_LOCK(utp);
+ if (utp->chip->type != UTP_TYPE_UNKNOWN && utp->state & UTP_ST_ACTIVE) {
+ ifmr->ifm_active = IFM_ATM | utp->ifatm->mib.media;
+
+ switch (utp->carrier) {
+
+ case UTP_CARR_OK:
+ ifmr->ifm_status = IFM_AVALID | IFM_ACTIVE;
+ break;
+
+ case UTP_CARR_LOST:
+ ifmr->ifm_status = IFM_AVALID;
+ break;
+
+ default:
+ ifmr->ifm_status = 0;
+ break;
+ }
+ if (utp->state & UTP_ST_SDH) {
+ ifmr->ifm_active |= IFM_ATM_SDH;
+ ifmr->ifm_current |= IFM_ATM_SDH;
+ }
+ if (utp->state & UTP_ST_UNASS) {
+ ifmr->ifm_active |= IFM_ATM_UNASSIGNED;
+ ifmr->ifm_current |= IFM_ATM_UNASSIGNED;
+ }
+ if (utp->state & UTP_ST_NOSCRAMB) {
+ ifmr->ifm_active |= IFM_ATM_NOSCRAMB;
+ ifmr->ifm_current |= IFM_ATM_NOSCRAMB;
+ }
+ } else {
+ ifmr->ifm_active = 0;
+ ifmr->ifm_status = 0;
+ }
+ UTP_UNLOCK(utp);
+}
+
+/*
+ * Initialize media from the mib
+ */
+void
+utopia_init_media(struct utopia *utp)
+{
+
+ ifmedia_removeall(utp->media);
+ ifmedia_add(utp->media, IFM_ATM | utp->ifatm->mib.media, 0, NULL);
+ ifmedia_set(utp->media, IFM_ATM | utp->ifatm->mib.media);
+}
+
+/*
+ * Reset all media
+ */
+void
+utopia_reset_media(struct utopia *utp)
+{
+
+ ifmedia_removeall(utp->media);
+}
+
+/*
+ * This is called by the driver as soon as the SUNI registers are accessible.
+ * This may be either in the attach routine or the init routine of the driver.
+ */
+int
+utopia_start(struct utopia *utp)
+{
+ uint8_t reg;
+ int err;
+ u_int n = 1;
+
+ if ((err = READREGS(utp, SUNI_REGO_MRESET, &reg, &n)) != 0)
+ return (err);
+
+ switch (reg & SUNI_REGM_MRESET_TYPE) {
+
+ case SUNI_REGM_MRESET_TYPE_622:
+ utp->chip = &chip_622;
+ break;
+
+ case SUNI_REGM_MRESET_TYPE_LITE:
+ utp->chip = &chip_lite;
+ break;
+
+ case SUNI_REGM_MRESET_TYPE_ULTRA:
+ utp->chip = &chip_ultra;
+ break;
+
+ default:
+ if (reg == (IDTPHY_REGM_MCR_DRIC | IDTPHY_REGM_MCR_EI))
+ utp->chip = &chip_idt77105;
+ else {
+ if_printf(&utp->ifatm->ifnet,
+ "unknown ATM-PHY chip %#x\n", reg);
+ utp->chip = &chip_unknown;
+ }
+ break;
+ }
+ utp->state |= UTP_ST_ACTIVE;
+ return (0);
+}
+
+/*
+ * Stop the chip
+ */
+void
+utopia_stop(struct utopia *utp)
+{
+ utp->state &= ~UTP_ST_ACTIVE;
+}
+
+/*
+ * Handle the sysctls
+ */
+static int
+utopia_sysctl_regs(SYSCTL_HANDLER_ARGS)
+{
+ struct utopia *utp = (struct utopia *)arg1;
+ int error;
+ u_int n;
+ uint8_t *val;
+ uint8_t new[3];
+
+ if ((n = utp->chip->nregs) == 0)
+ return (EIO);
+ val = malloc(sizeof(uint8_t) * n, M_TEMP, M_WAITOK);
+
+ UTP_LOCK(utp);
+ error = READREGS(utp, 0, val, &n);
+ UTP_UNLOCK(utp);
+
+ if (error) {
+ free(val, M_TEMP);
+ return (error);
+ }
+
+ error = SYSCTL_OUT(req, val, sizeof(uint8_t) * n);
+ free(val, M_TEMP);
+ if (error != 0 || req->newptr == NULL)
+ return (error);
+
+ error = SYSCTL_IN(req, new, sizeof(new));
+ if (error)
+ return (error);
+
+ UTP_LOCK(utp);
+ error = WRITEREG(utp, new[0], new[1], new[2]);
+ UTP_UNLOCK(utp);
+
+ return (error);
+}
+
+/*
+ * Handle the loopback sysctl
+ */
+static int
+utopia_sysctl_loopback(SYSCTL_HANDLER_ARGS)
+{
+ struct utopia *utp = (struct utopia *)arg1;
+ int error;
+ u_int loopback;
+
+ error = SYSCTL_OUT(req, &utp->loopback, sizeof(u_int));
+ if (error != 0 || req->newptr == NULL)
+ return (error);
+
+ error = SYSCTL_IN(req, &loopback, sizeof(u_int));
+ if (error)
+ return (error);
+
+ UTP_LOCK(utp);
+ error = utopia_set_loopback(utp, loopback);
+ UTP_UNLOCK(utp);
+
+ return (error);
+}
+
+/*
+ * Handle the type sysctl
+ */
+static int
+utopia_sysctl_type(SYSCTL_HANDLER_ARGS)
+{
+ struct utopia *utp = (struct utopia *)arg1;
+
+ return (SYSCTL_OUT(req, &utp->chip->type, sizeof(utp->chip->type)));
+}
+
+/*
+ * Handle the name sysctl
+ */
+static int
+utopia_sysctl_name(SYSCTL_HANDLER_ARGS)
+{
+ struct utopia *utp = (struct utopia *)arg1;
+
+ return (SYSCTL_OUT(req, utp->chip->name, strlen(utp->chip->name) + 1));
+}
+
+/*
+ * Initialize the state. This is called from the drivers attach
+ * function. The mutex must be already initialized.
+ */
+int
+utopia_attach(struct utopia *utp, struct ifatm *ifatm, struct ifmedia *media,
+ struct mtx *lock, struct sysctl_ctx_list *ctx,
+ struct sysctl_oid_list *children, const struct utopia_methods *m)
+{
+
+ bzero(utp, sizeof(*utp));
+ utp->ifatm = ifatm;
+ utp->methods = m;
+ utp->media = media;
+ utp->lock = lock;
+ utp->chip = &chip_unknown;
+
+ ifmedia_init(media,
+ IFM_ATM_SDH | IFM_ATM_UNASSIGNED | IFM_ATM_NOSCRAMB,
+ utopia_media_change, utopia_media_status);
+
+ if (SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "phy_regs",
+ CTLFLAG_RW | CTLTYPE_OPAQUE, utp, 0, utopia_sysctl_regs, "S",
+ "phy registers") == NULL)
+ return (-1);
+
+ if (SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "phy_loopback",
+ CTLFLAG_RW | CTLTYPE_UINT, utp, 0, utopia_sysctl_loopback, "IU",
+ "phy loopback mode") == NULL)
+ return (-1);
+
+ if (SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "phy_type",
+ CTLFLAG_RD | CTLTYPE_UINT, utp, 0, utopia_sysctl_type, "IU",
+ "phy type") == NULL)
+ return (-1);
+
+ if (SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "phy_name",
+ CTLFLAG_RD | CTLTYPE_STRING, utp, 0, utopia_sysctl_name, "A",
+ "phy name") == NULL)
+ return (-1);
+
+ UTP_WLOCK_LIST();
+ LIST_INSERT_HEAD(&utopia_list, utp, link);
+ UTP_WUNLOCK_LIST();
+
+ utp->state |= UTP_ST_ATTACHED;
+ return (0);
+}
+
+/*
+ * Detach. We set a flag here, wakeup the daemon and let him do it.
+ * Here we need the lock for synchronisation with the daemon.
+ */
+void
+utopia_detach(struct utopia *utp)
+{
+
+ UTP_LOCK_ASSERT(utp);
+ if (utp->state & UTP_ST_ATTACHED) {
+ utp->state |= UTP_ST_DETACH;
+ while (utp->state & UTP_ST_DETACH) {
+ wakeup(&utopia_list);
+ msleep(utp, utp->lock, PZERO, "utopia_detach", hz);
+ }
+ }
+}
+
+/*
+ * The carrier state kernel proc for those adapters that do not interrupt.
+ *
+ * We assume, that utopia_attach can safely add a new utopia while we are going
+ * through the list without disturbing us (we lock the list while getting
+ * the address of the first element, adding is always done at the head).
+ * Removing is entirely handled here.
+ */
+static void
+utopia_daemon(void *arg __unused)
+{
+ struct utopia *utp, *next;
+
+ UTP_RLOCK_LIST();
+ while (utopia_kproc != NULL) {
+ utp = LIST_FIRST(&utopia_list);
+ UTP_RUNLOCK_LIST();
+
+ while (utp != NULL) {
+ mtx_lock(&Giant); /* XXX depend on MPSAFE */
+ UTP_LOCK(utp);
+ next = LIST_NEXT(utp, link);
+ if (utp->state & UTP_ST_DETACH) {
+ LIST_REMOVE(utp, link);
+ utp->state &= ~UTP_ST_DETACH;
+ wakeup_one(utp);
+ } else if ((utp->state & UTP_ST_ACTIVE) &&
+ (utp->flags & UTP_FL_POLL_CARRIER)) {
+ utopia_update_carrier(utp);
+ }
+ UTP_UNLOCK(utp);
+ mtx_unlock(&Giant); /* XXX depend on MPSAFE */
+ utp = next;
+ }
+
+ UTP_RLOCK_LIST();
+ msleep(&utopia_list, &utopia_list_mtx, PZERO, "utopia", hz);
+ }
+ wakeup_one(&utopia_list);
+ UTP_RUNLOCK_LIST();
+ mtx_lock(&Giant);
+ kthread_exit(0);
+}
+
+/*
+ * Module initialisation
+ */
+static int
+utopia_mod_init(module_t mod, int what, void *arg)
+{
+ int err;
+ struct proc *kp;
+
+ switch (what) {
+
+ case MOD_LOAD:
+ mtx_init(&utopia_list_mtx, "utopia list mutex", NULL, MTX_DEF);
+ err = kthread_create(utopia_daemon, NULL, &utopia_kproc,
+ RFHIGHPID, 0, "utopia");
+ if (err != 0) {
+ printf("cannot created utopia thread %d\n", err);
+ return (err);
+ }
+ break;
+
+ case MOD_UNLOAD:
+ UTP_WLOCK_LIST();
+ if ((kp = utopia_kproc) != NULL) {
+ utopia_kproc = NULL;
+ wakeup_one(&utopia_list);
+ PROC_LOCK(kp);
+ UTP_WUNLOCK_LIST();
+ msleep(kp, &kp->p_mtx, PWAIT, "utopia_destroy", 0);
+ PROC_UNLOCK(kp);
+ } else
+ UTP_WUNLOCK_LIST();
+ mtx_destroy(&utopia_list_mtx);
+ break;
+ }
+ return (0);
+}
+
+static moduledata_t utopia_mod = {
+ "utopia",
+ utopia_mod_init,
+ 0
+};
+
+DECLARE_MODULE(utopia, utopia_mod, SI_SUB_INIT_IF, SI_ORDER_ANY);
+MODULE_VERSION(utopia, 1);
OpenPOWER on IntegriCloud