patch-2.1.23 linux/drivers/ap1000/plc.c
Next file: linux/drivers/ap1000/plc.h
Previous file: linux/drivers/ap1000/mac.h
Back to the patch index
Back to the overall index
- Lines: 394
- Date:
Sun Jan 26 12:07:10 1997
- Orig file:
v2.1.22/linux/drivers/ap1000/plc.c
- Orig date:
Thu Jan 1 02:00:00 1970
diff -u --recursive --new-file v2.1.22/linux/drivers/ap1000/plc.c linux/drivers/ap1000/plc.c
@@ -0,0 +1,393 @@
+ /*
+ * Copyright 1996 The Australian National University.
+ * Copyright 1996 Fujitsu Laboratories Limited
+ *
+ * This software may be distributed under the terms of the Gnu
+ * Public License version 2 or later
+ */
+/*
+ * Routines for controlling the Am79c864 physical layer controller.
+ *
+ * This chip implements some parts of the FDDI SMT standard
+ * (PCM: physical connection management, LEM: link error monitor, etc.)
+ * as well as the FDDI PHY standard.
+ */
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include "apfddi.h"
+#include "smt-types.h"
+#include "am79c864.h"
+#include "plc.h"
+#include "apfddi-reg.h"
+
+typedef enum {
+ off,
+ signalling,
+ doing_lct,
+ joining,
+ active
+} PlcPhase;
+
+struct plc_state {
+ LoopbackType loopback;
+ char t_val[16];
+ char r_val[16];
+ int n;
+ PortType peer_type;
+ PlcPhase phase;
+};
+
+struct plc_info *this_plc_info;
+struct plc_state this_plc_state;
+
+void plc_init(struct plc_info *pip)
+{
+ int class, x;
+ struct plc_state *psp = &this_plc_state;
+
+ this_plc_info = pip;
+
+ /* first turn it off, clear registers */
+ class = pip->port_type == pt_s? CB_CLASS_S: 0;
+ plc->ctrl_b = CB_PC_STOP + class;
+ plc->intr_mask = IE_NP_ERROR;
+ x = plc->intr_event; /* these register clear when read */
+ x = plc->viol_sym_ct;
+ x = plc->min_idle_ct;
+ x = plc->link_err_ct;
+
+ /* initialize registers */
+ plc->ctrl_a = 0;
+ plc->ctrl_b = class;
+ plc->c_min = pip->c_min >> 8;
+ plc->tl_min = pip->tl_min >> 8;
+ plc->tb_min = pip->tb_min >> 8;
+ plc->t_out = pip->t_out >> 8;
+ plc->t_scrub = pip->t_scrub >> 8;
+ plc->ns_max = pip->ns_max >> 2;
+
+ psp->phase = off;
+}
+
+int
+plc_inited(struct plc_info *pip)
+{
+ int class, x;
+ struct plc_state *psp = &this_plc_state;
+
+ class = pip->port_type == pt_s? CB_CLASS_S: 0;
+ if ((plc->ctrl_a & (CA_LOOPBACK|CA_FOT_OFF|CA_EB_LOOP|CA_LM_LOOP)) != 0)
+ return 1;
+ if ((plc->ctrl_b & (CB_CONFIG_CTRL|CB_CLASS_S|CB_PC_MAINT)) != class)
+ return 2;
+ if (plc->status_a & SA_SIG_DETECT)
+ return 3;
+ if ((plc->status_b & (SB_PCI_STATE|SB_PCM_STATE))
+ != (SB_PCI_STATE_INSERTED|SB_PCM_STATE_ACTIVE))
+ return 4;
+
+ /* all seems OK, reset the timers and counters just to be sure */
+ plc->intr_mask = IE_NP_ERROR;
+ x = plc->intr_event; /* these register clear when read */
+ x = plc->viol_sym_ct;
+ x = plc->min_idle_ct;
+ x = plc->link_err_ct;
+
+ plc->c_min = pip->c_min >> 8;
+ plc->tl_min = pip->tl_min >> 8;
+ plc->tb_min = pip->tb_min >> 8;
+ plc->t_out = pip->t_out >> 8;
+ plc->t_scrub = pip->t_scrub >> 8;
+ plc->ns_max = pip->ns_max >> 2;
+
+ psp->phase = active;
+ /* XXX should initialize other fields of this_plc_state */
+
+ return 0;
+}
+
+void plc_sleep(void)
+{
+}
+
+void pc_start(LoopbackType loopback)
+{
+ int x;
+ struct plc_info *pip = this_plc_info;
+ struct plc_state *psp = &this_plc_state;
+
+ /* make sure it's off */
+ plc->ctrl_b &= ~CB_PCM_CTRL;
+ plc->ctrl_b |= CB_PC_STOP;
+
+ /* set up loopback required */
+ psp->loopback = loopback;
+ x = 0;
+ switch (loopback) {
+ case loop_plc_lm:
+ x = CA_LM_LOOP;
+ break;
+ case loop_plc_eb:
+ x = CA_EB_LOOP;
+ break;
+ case loop_pdx:
+ x = CA_LOOPBACK;
+ break;
+ default:
+ x = 0;
+ }
+ plc->ctrl_a = x;
+
+ /* set up bits to be exchanged */
+ psp->t_val[0] = 0;
+ psp->t_val[1] = ((int) pip->port_type >> 1) & 1;
+ psp->t_val[2] = (int) pip->port_type & 1;
+ psp->t_val[4] = 0; /* XXX assume we want short LCT */
+ psp->t_val[5] = 0;
+ psp->t_val[6] = 0; /* XXX too lazy to fire up my MAC for LCT */
+ psp->t_val[8] = 0; /* XXX don't wanna local loop */
+ psp->t_val[9] = 1; /* gotta MAC on port output */
+
+ pc_restart();
+}
+
+void pc_restart(void)
+{
+ struct plc_state *psp = &this_plc_state;
+
+ if (psp->phase != off)
+ printk("restarting pcm\n");
+ if (psp->phase == active)
+ set_cf_join(0); /* we're down :-( */
+
+ psp->n = 0;
+ plc->vec_length = 3 - 1;
+ plc->xmit_vector = psp->t_val[0] + (psp->t_val[1] << 1)
+ + (psp->t_val[2] << 2);
+
+ plc->intr_mask = IE_NP_ERROR | IE_PCM_BREAK | IE_PCM_CODE;
+ plc->ctrl_b &= ~CB_PCM_CTRL;
+ plc->ctrl_b |= CB_PC_START; /* light blue paper and stand clear */
+
+ psp->phase = signalling;
+}
+
+void pc_stop(void)
+{
+ struct plc_state *psp = &this_plc_state;
+
+ if (psp->phase == active)
+ set_cf_join(0);
+ plc->ctrl_b &= ~CB_PCM_CTRL;
+ plc->ctrl_b |= CB_PC_STOP;
+ plc->intr_mask = IE_NP_ERROR;
+ psp->phase = off;
+}
+
+void plc_poll(void)
+{
+ struct plc_state *psp = &this_plc_state;
+ int events, i;
+
+ if ((*csr0 & CS0_PHY_IRQ) == 0)
+ return;
+ events = plc->intr_event & plc->intr_mask;
+ if (events & IE_NP_ERROR) {
+ printk("plc: NP error!\n");
+ }
+ if (events & IE_PCM_BREAK) {
+ i = plc->status_b & SB_BREAK_REASON;
+ if (i > SB_BREAK_REASON_START) {
+ if (psp->phase == signalling || psp->phase == doing_lct)
+ pcm_dump_rtcodes();
+ printk("pcm: break reason %d\n", i);
+ if (psp->phase != off)
+ pc_restart();
+ /* XXX need to check for trace? */
+ }
+ }
+ if (events & IE_PCM_CODE) {
+ if (psp->phase == signalling)
+ pcm_pseudo_code();
+ else if (psp->phase == doing_lct)
+ pcm_lct_done();
+ else
+ printk("XXX pcm_code interrupt in phase %d?\n", psp->phase);
+ }
+ if (events & IE_PCM_ENABLED) {
+ if (psp->phase == joining)
+ pcm_enabled();
+ else
+ printk("XXX pcm_enabled interrupt in phase %d?\n", psp->phase);
+ }
+ if (events & IE_TRACE_PROP) {
+ if (psp->phase == active)
+ pcm_trace_prop();
+ else
+ printk("XXX trace_prop interrupt in phase %d\n", psp->phase);
+ }
+}
+
+void pcm_pseudo_code(void)
+{
+ struct plc_info *pip = this_plc_info;
+ struct plc_state *psp = &this_plc_state;
+ int i, nb, lct, hislct;
+
+ /* unpack the bits from the peer */
+ nb = plc->vec_length + 1;
+ i = plc->rcv_vector;
+ do {
+ psp->r_val[psp->n++] = i & 1;
+ i >>= 1;
+ } while (--nb > 0);
+
+ /* send some more, do LCT, whatever */
+ switch (psp->n) {
+ case 3:
+ /*
+ * Got escape flag, port type; send compatibility,
+ * LCT duration, MAC for LCT flag.
+ */
+ if (psp->r_val[0]) {
+ /* help! what do I do now? */
+ pcm_dump_rtcodes();
+ pc_restart();
+ break;
+ }
+ psp->peer_type = (PortType) ((psp->r_val[1] << 1) + psp->r_val[2]);
+ /* XXX we're type S, we talk to anybody */
+ psp->t_val[3] = 1;
+
+ plc->vec_length = 4 - 1;
+ plc->xmit_vector = psp->t_val[3] + (psp->t_val[4] << 1)
+ + (psp->t_val[5] << 2) + (psp->t_val[6] << 3);
+ break;
+
+ case 7:
+ /*
+ * Got compatibility, LCT duration, MAC for LCT flag;
+ * time to do the LCT.
+ */
+ lct = (psp->t_val[4] << 1) + psp->t_val[5];
+ hislct = (psp->r_val[4] << 1) + psp->r_val[5];
+ if (hislct > lct)
+ lct = hislct;
+
+ /* set LCT duration */
+ switch (lct) {
+ case 0:
+ plc->lc_length = pip->lc_short >> 8;
+ plc->ctrl_b &= ~CB_LONG_LCT;
+ break;
+ case 1:
+ plc->lc_length = pip->lc_medium >> 8;
+ plc->ctrl_b &= ~CB_LONG_LCT;
+ break;
+ case 2:
+ plc->ctrl_b |= CB_LONG_LCT;
+ /* XXX set up a timeout for pip->lc_long */
+ break;
+ case 3:
+ plc->ctrl_b |= CB_LONG_LCT;
+ /* XXX set up a timeout for pip->lc_extended */
+ break;
+ }
+
+ /* start the LCT */
+ i = plc->link_err_ct; /* clear the register */
+ plc->ctrl_b &= ~CB_PC_LCT;
+ /* XXX assume we're not using the MAC for LCT;
+ if he's got a MAC, loop his stuff back, otherwise send idle. */
+ if (psp->r_val[6])
+ plc->ctrl_b |= CB_PC_LCT_LOOP;
+ else
+ plc->ctrl_b |= CB_PC_LCT_IDLE;
+ psp->phase = doing_lct;
+ break;
+
+ case 8:
+ /*
+ * Got LCT result, send MAC for local loop and MAC on port
+ * output flags.
+ */
+ if (psp->t_val[7] || psp->r_val[7]) {
+ printk("LCT failed, restarting.\n");
+ /* LCT failed - do at least a medium length test next time. */
+ if (psp->t_val[4] == 0 && psp->t_val[5] == 0)
+ psp->t_val[5] = 1;
+ pcm_dump_rtcodes();
+ pc_restart();
+ break;
+ }
+ plc->vec_length = 2 - 1;
+ plc->xmit_vector = psp->t_val[8] + (psp->t_val[9] << 1);
+ break;
+
+ case 10:
+ /*
+ * Got MAC for local loop and MAC on port output flags.
+ * Let's join.
+ */
+ plc->intr_mask = IE_NP_ERROR | IE_PCM_BREAK | IE_PCM_ENABLED;
+ plc->ctrl_b |= CB_PC_JOIN;
+ psp->phase = joining;
+ /* printk("pcm: joining\n"); */
+ break;
+
+ default:
+ printk("pcm_pseudo_code bug: n = %d\n", psp->n);
+ }
+}
+
+void pcm_lct_done(void)
+{
+ struct plc_state *psp = &this_plc_state;
+ int i;
+
+ i = plc->link_err_ct;
+ psp->t_val[7] = i > 0;
+ printk("pcm: lct %s (%d errors)\n", psp->t_val[7]? "failed": "passed", i);
+ plc->ctrl_b &= ~(CB_PC_LCT | CB_LONG_LCT);
+ plc->vec_length = 1 - 1;
+ plc->xmit_vector = psp->t_val[7];
+ psp->phase = signalling;
+}
+
+void pcm_dump_rtcodes(void)
+{
+ struct plc_state *psp = &this_plc_state;
+ int i;
+
+ if (psp->n > 0) {
+ printk("pcm signalling interrupted after %d bits:\nt_val:", psp->n);
+ for (i = 0; i < psp->n; ++i)
+ printk(" %d", psp->t_val[i]);
+ printk("\nr_val:");
+ for (i = 0; i < psp->n; ++i)
+ printk(" %d", psp->r_val[i]);
+ printk("\n");
+ }
+}
+
+void pcm_enabled(void)
+{
+ struct plc_state *psp = &this_plc_state;
+ int i;
+
+ printk("pcm: enabled\n");
+ psp->phase = active;
+ i = plc->link_err_ct; /* clear the register */
+ /* XXX should set up LEM here */
+ /* XXX do we want to count violation symbols, minimum idle gaps,
+ or elasticity buffer errors? */
+ plc->intr_mask = IE_NP_ERROR | IE_PCM_BREAK | IE_TRACE_PROP;
+ set_cf_join(1); /* we're up :-) */
+}
+
+void pcm_trace_prop(void)
+{
+ /* XXX help! what do I do now? */
+ pc_stop();
+}
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov