1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /******************************************************************************
4 * (C)Copyright 1998,1999 SysKonnect,
5 * a business unit of Schneider & Koch & Co. Datensysteme GmbH.
7 * See the file "skfddi.c" for further information.
9 * The information in this file is provided "AS IS" without warranty.
11 ******************************************************************************/
15 Physical Connection Management
19 * Hardware independent state machine implemantation
20 * The following external SMT functions are referenced :
26 * The following external HW dependent functions are referenced :
30 * The following HW dependent events are required :
44 #include "h/supern_2.h"
46 #include "h/smtstate.h"
49 static const char ID_sccs
[] = "@(#)pcmplc.c 2.55 99/08/05 (C) SK " ;
53 extern int snmp_fddi_trap(
55 struct s_smc
* smc
, int type
, int index
60 extern int plc_is_installed(
71 #define GO_STATE(x) (mib->fddiPORTPCMState = (x)|AFLAG)
72 #define ACTIONS_DONE() (mib->fddiPORTPCMState &= ~AFLAG)
73 #define ACTIONS(x) (x|AFLAG)
90 * symbolic state names
92 static const char * const pcm_states
[] = {
93 "PC0_OFF","PC1_BREAK","PC2_TRACE","PC3_CONNECT","PC4_NEXT",
94 "PC5_SIGNAL","PC6_JOIN","PC7_VERIFY","PC8_ACTIVE","PC9_MAINT"
98 * symbolic event names
100 static const char * const pcm_events
[] = {
101 "NONE","PC_START","PC_STOP","PC_LOOP","PC_JOIN","PC_SIGNAL",
102 "PC_REJECT","PC_MAINT","PC_TRACE","PC_PDR",
103 "PC_ENABLE","PC_DISABLE",
104 "PC_QLS","PC_ILS","PC_MLS","PC_HLS","PC_LS_PDR","PC_LS_NONE",
105 "PC_TIMEOUT_TB_MAX","PC_TIMEOUT_TB_MIN",
106 "PC_TIMEOUT_C_MIN","PC_TIMEOUT_T_OUT",
107 "PC_TIMEOUT_TL_MIN","PC_TIMEOUT_T_NEXT","PC_TIMEOUT_LCT",
113 * PCL-S control register
114 * this register in the PLC-S controls the scrambling parameters
116 #define PLCS_CONTROL_C_U 0
117 #define PLCS_CONTROL_C_S (PL_C_SDOFF_ENABLE | PL_C_SDON_ENABLE | \
119 #define PLCS_FASSERT_U 0
120 #define PLCS_FASSERT_S 0xFd76 /* 52.0 us */
121 #define PLCS_FDEASSERT_U 0
122 #define PLCS_FDEASSERT_S 0
125 * PCL-S control register
126 * this register in the PLC-S controls the scrambling parameters
127 * can be patched for ANSI compliance if standard changes
129 static const u_char plcs_control_c_u
[17] = "PLC_CNTRL_C_U=\0\0" ;
130 static const u_char plcs_control_c_s
[17] = "PLC_CNTRL_C_S=\01\02" ;
132 #define PLCS_CONTROL_C_U (plcs_control_c_u[14] | (plcs_control_c_u[15]<<8))
133 #define PLCS_CONTROL_C_S (plcs_control_c_s[14] | (plcs_control_c_s[15]<<8))
134 #endif /* nMOT_ELM */
139 /* struct definition see 'cmtdef.h' (also used by CFM) */
150 #define LCT_LEM_MAX 255
153 * PLC timing parameter
156 #define PLC_MS(m) ((int)((0x10000L-(m*100000L/2048))))
157 #define SLOW_TL_MIN PLC_MS(6)
158 #define SLOW_C_MIN PLC_MS(10)
160 static const struct plt
{
161 int timer
; /* relative plc timer address */
162 int para
; /* default timing parameters */
164 { PL_C_MIN
, SLOW_C_MIN
}, /* min t. to remain Connect State */
165 { PL_TL_MIN
, SLOW_TL_MIN
}, /* min t. to transmit a Line State */
166 { PL_TB_MIN
, TP_TB_MIN
}, /* min break time */
167 { PL_T_OUT
, TP_T_OUT
}, /* Signaling timeout */
168 { PL_LC_LENGTH
, TP_LC_LENGTH
}, /* Link Confidence Test Time */
169 { PL_T_SCRUB
, TP_T_SCRUB
}, /* Scrub Time == MAC TVX time ! */
170 { PL_NS_MAX
, TP_NS_MAX
}, /* max t. that noise is tolerated */
179 * Do we need the EBUF error during signaling, too, to detect SUPERNET_3
182 static const int plc_imsk_na
= PL_PCM_CODE
| PL_TRACE_PROP
| PL_PCM_BREAK
|
183 PL_PCM_ENABLED
| PL_SELF_TEST
| PL_EBUF_ERR
;
184 #else /* SUPERNET_3 */
186 * We do NOT need the elasticity buffer error during signaling.
188 static int plc_imsk_na
= PL_PCM_CODE
| PL_TRACE_PROP
| PL_PCM_BREAK
|
189 PL_PCM_ENABLED
| PL_SELF_TEST
;
190 #endif /* SUPERNET_3 */
191 static const int plc_imsk_act
= PL_PCM_CODE
| PL_TRACE_PROP
| PL_PCM_BREAK
|
192 PL_PCM_ENABLED
| PL_SELF_TEST
| PL_EBUF_ERR
;
194 /* internal functions */
195 static void pcm_fsm(struct s_smc
*smc
, struct s_phy
*phy
, int cmd
);
196 static void pc_rcode_actions(struct s_smc
*smc
, int bit
, struct s_phy
*phy
);
197 static void pc_tcode_actions(struct s_smc
*smc
, const int bit
, struct s_phy
*phy
);
198 static void reset_lem_struct(struct s_phy
*phy
);
199 static void plc_init(struct s_smc
*smc
, int p
);
200 static void sm_ph_lem_start(struct s_smc
*smc
, int np
, int threshold
);
201 static void sm_ph_lem_stop(struct s_smc
*smc
, int np
);
202 static void sm_ph_linestate(struct s_smc
*smc
, int phy
, int ls
);
203 static void real_init_plc(struct s_smc
*smc
);
206 * SMT timer interface
209 static void start_pcm_timer0(struct s_smc
*smc
, u_long value
, int event
,
212 phy
->timer0_exp
= FALSE
; /* clear timer event flag */
213 smt_timer_start(smc
,&phy
->pcm_timer0
,value
,
214 EV_TOKEN(EVENT_PCM
+phy
->np
,event
)) ;
217 * SMT timer interface
220 static void stop_pcm_timer0(struct s_smc
*smc
, struct s_phy
*phy
)
222 if (phy
->pcm_timer0
.tm_active
)
223 smt_timer_stop(smc
,&phy
->pcm_timer0
) ;
227 init PCM state machine (called by driver)
228 clear all PCM vars and flags
230 void pcm_init(struct s_smc
*smc
)
235 struct fddi_mib_p
*mib
;
237 for (np
= 0,phy
= smc
->y
; np
< NUMPHYS
; np
++,phy
++) {
238 /* Indicates the type of PHY being used */
240 mib
->fddiPORTPCMState
= ACTIONS(PC0_OFF
) ;
242 switch (smc
->s
.sas
) {
245 mib
->fddiPORTMy_Type
= (np
== PS
) ? TS
: TM
;
248 mib
->fddiPORTMy_Type
= (np
== PA
) ? TA
:
249 (np
== PB
) ? TB
: TM
;
252 mib
->fddiPORTMy_Type
= TM
;
256 mib
->fddiPORTMy_Type
= (np
== PS
) ? TS
: TNONE
;
257 mib
->fddiPORTHardwarePresent
= (np
== PS
) ? TRUE
:
260 smc
->y
[PA
].mib
->fddiPORTPCMState
= PC0_OFF
;
262 smc
->y
[PB
].mib
->fddiPORTPCMState
= PC0_OFF
;
266 mib
->fddiPORTMy_Type
= (np
== PB
) ? TB
: TA
;
273 phy
->pmd_scramble
= 0 ;
274 switch (phy
->pmd_type
[PMD_SK_PMD
]) {
276 mib
->fddiPORTPMDClass
= MIB_PMDCLASS_MULTI
;
279 mib
->fddiPORTPMDClass
= MIB_PMDCLASS_LCF
;
282 mib
->fddiPORTPMDClass
= MIB_PMDCLASS_TP
;
285 mib
->fddiPORTPMDClass
= MIB_PMDCLASS_TP
;
286 phy
->pmd_scramble
= TRUE
;
289 mib
->fddiPORTPMDClass
= MIB_PMDCLASS_TP
;
290 phy
->pmd_scramble
= TRUE
;
293 mib
->fddiPORTPMDClass
= MIB_PMDCLASS_SINGLE1
;
296 mib
->fddiPORTPMDClass
= MIB_PMDCLASS_SINGLE2
;
299 mib
->fddiPORTPMDClass
= MIB_PMDCLASS_SINGLE2
;
302 mib
->fddiPORTPMDClass
= MIB_PMDCLASS_SINGLE1
;
305 mib
->fddiPORTPMDClass
= MIB_PMDCLASS_UNKNOWN
;
308 mib
->fddiPORTPMDClass
= MIB_PMDCLASS_TP
;
311 mib
->fddiPORTPMDClass
= MIB_PMDCLASS_TP
;
314 mib
->fddiPORTPMDClass
= MIB_PMDCLASS_UNKNOWN
;
318 * A and B port can be on primary and secondary path
320 switch (mib
->fddiPORTMy_Type
) {
322 mib
->fddiPORTAvailablePaths
|= MIB_PATH_S
;
323 mib
->fddiPORTRequestedPaths
[1] = MIB_P_PATH_LOCAL
;
324 mib
->fddiPORTRequestedPaths
[2] =
326 MIB_P_PATH_CON_ALTER
|
327 MIB_P_PATH_SEC_PREFER
;
328 mib
->fddiPORTRequestedPaths
[3] =
330 MIB_P_PATH_CON_ALTER
|
331 MIB_P_PATH_SEC_PREFER
|
335 mib
->fddiPORTAvailablePaths
|= MIB_PATH_S
;
336 mib
->fddiPORTRequestedPaths
[1] = MIB_P_PATH_LOCAL
;
337 mib
->fddiPORTRequestedPaths
[2] =
339 MIB_P_PATH_PRIM_PREFER
;
340 mib
->fddiPORTRequestedPaths
[3] =
342 MIB_P_PATH_PRIM_PREFER
|
343 MIB_P_PATH_CON_PREFER
|
347 mib
->fddiPORTAvailablePaths
|= MIB_PATH_S
;
348 mib
->fddiPORTRequestedPaths
[1] = MIB_P_PATH_LOCAL
;
349 mib
->fddiPORTRequestedPaths
[2] =
351 MIB_P_PATH_CON_ALTER
|
352 MIB_P_PATH_PRIM_PREFER
;
353 mib
->fddiPORTRequestedPaths
[3] =
355 MIB_P_PATH_CON_ALTER
|
356 MIB_P_PATH_PRIM_PREFER
;
359 mib
->fddiPORTRequestedPaths
[1] = MIB_P_PATH_LOCAL
;
360 mib
->fddiPORTRequestedPaths
[2] =
362 MIB_P_PATH_SEC_ALTER
|
363 MIB_P_PATH_PRIM_ALTER
;
364 mib
->fddiPORTRequestedPaths
[3] = 0 ;
368 phy
->pc_lem_fail
= FALSE
;
369 mib
->fddiPORTPCMStateX
= mib
->fddiPORTPCMState
;
370 mib
->fddiPORTLCTFail_Ct
= 0 ;
371 mib
->fddiPORTBS_Flag
= 0 ;
372 mib
->fddiPORTCurrentPath
= MIB_PATH_ISOLATED
;
373 mib
->fddiPORTNeighborType
= TNONE
;
379 phy
->phy_name
= '0' + np
- PM
;
381 phy
->phy_name
= 'A' + np
;
382 phy
->wc_flag
= FALSE
; /* set by SMT */
383 memset((char *)&phy
->lem
,0,sizeof(struct lem_counter
)) ;
384 reset_lem_struct(phy
) ;
385 memset((char *)&phy
->plc
,0,sizeof(struct s_plc
)) ;
386 phy
->plc
.p_state
= PS_OFF
;
387 for (i
= 0 ; i
< NUMBITS
; i
++) {
394 void init_plc(struct s_smc
*smc
)
400 * this is an obsolete public entry point that has to remain
401 * for compat. It is used by various drivers.
402 * the work is now done in real_init_plc()
403 * which is called from pcm_init() ;
407 static void real_init_plc(struct s_smc
*smc
)
411 for (p
= 0 ; p
< NUMPHYS
; p
++)
415 static void plc_init(struct s_smc
*smc
, int p
)
419 int rev
; /* Revision of PLC-x */
422 /* transit PCM state machine to MAINT state */
423 outpw(PLC(p
,PL_CNTRL_B
),0) ;
424 outpw(PLC(p
,PL_CNTRL_B
),PL_PCM_STOP
) ;
425 outpw(PLC(p
,PL_CNTRL_A
),0) ;
428 * if PLC-S then set control register C
431 rev
= inpw(PLC(p
,PL_STATUS_A
)) & PLC_REV_MASK
;
432 if (rev
!= PLC_REVISION_A
)
435 if (smc
->y
[p
].pmd_scramble
) {
436 outpw(PLC(p
,PL_CNTRL_C
),PLCS_CONTROL_C_S
) ;
438 outpw(PLC(p
,PL_T_FOT_ASS
),PLCS_FASSERT_S
) ;
439 outpw(PLC(p
,PL_T_FOT_DEASS
),PLCS_FDEASSERT_S
) ;
443 outpw(PLC(p
,PL_CNTRL_C
),PLCS_CONTROL_C_U
) ;
445 outpw(PLC(p
,PL_T_FOT_ASS
),PLCS_FASSERT_U
) ;
446 outpw(PLC(p
,PL_T_FOT_DEASS
),PLCS_FDEASSERT_U
) ;
454 for ( i
= 0 ; pltm
[i
].timer
; i
++) /* set timer parameter reg */
455 outpw(PLC(p
,pltm
[i
].timer
),pltm
[i
].para
) ;
457 (void)inpw(PLC(p
,PL_INTR_EVENT
)) ; /* clear interrupt event reg */
458 plc_clear_irq(smc
,p
) ;
459 outpw(PLC(p
,PL_INTR_MASK
),plc_imsk_na
); /* enable non active irq's */
462 * if PCM is configured for class s, it will NOT go to the
463 * REMOVE state if offline (page 3-36;)
464 * in the concentrator, all inactive PHYS always must be in
466 * there's no real need to use this feature at all ..
469 if ((smc
->s
.sas
== SMT_SAS
) && (p
== PS
)) {
470 outpw(PLC(p
,PL_CNTRL_B
),PL_CLASS_S
) ;
476 * control PCM state machine
478 static void plc_go_state(struct s_smc
*smc
, int p
, int state
)
485 port
= (HW_PTR
) (PLC(p
,PL_CNTRL_B
)) ;
486 val
= inpw(port
) & ~(PL_PCM_CNTRL
| PL_MAINT
) ;
488 outpw(port
,val
| state
) ;
492 * read current line state (called by ECM & PCM)
494 int sm_pm_get_ls(struct s_smc
*smc
, int phy
)
499 if (!plc_is_installed(smc
,phy
))
503 state
= inpw(PLC(phy
,PL_STATUS_A
)) & PL_LINE_ST
;
527 static int plc_send_bits(struct s_smc
*smc
, struct s_phy
*phy
, int len
)
529 int np
= phy
->np
; /* PHY index */
535 /* create bit vector */
536 for (i
= len
-1,n
= 0 ; i
>= 0 ; i
--) {
537 n
= (n
<<1) | phy
->t_val
[phy
->bitn
+i
] ;
539 if (inpw(PLC(np
,PL_STATUS_B
)) & PL_PCM_SIGNAL
) {
541 printf("PL_PCM_SIGNAL is set\n") ;
545 /* write bit[n] & length = 1 to regs */
546 outpw(PLC(np
,PL_VECTOR_LEN
),len
-1) ; /* len=nr-1 */
547 outpw(PLC(np
,PL_XMIT_VECTOR
),n
) ;
551 if (smc
->debug
.d_plc
& 0x80)
553 if (debug
.d_plc
& 0x80)
555 printf("SIGNALING bit %d .. %d\n",phy
->bitn
,phy
->bitn
+len
-1) ;
564 void plc_config_mux(struct s_smc
*smc
, int mux
)
566 if (smc
->s
.sas
!= SMT_DAS
)
568 if (mux
== MUX_WRAPB
) {
569 SETMASK(PLC(PA
,PL_CNTRL_B
),PL_CONFIG_CNTRL
,PL_CONFIG_CNTRL
) ;
570 SETMASK(PLC(PA
,PL_CNTRL_A
),PL_SC_REM_LOOP
,PL_SC_REM_LOOP
) ;
573 CLEAR(PLC(PA
,PL_CNTRL_B
),PL_CONFIG_CNTRL
) ;
574 CLEAR(PLC(PA
,PL_CNTRL_A
),PL_SC_REM_LOOP
) ;
576 CLEAR(PLC(PB
,PL_CNTRL_B
),PL_CONFIG_CNTRL
) ;
577 CLEAR(PLC(PB
,PL_CNTRL_A
),PL_SC_REM_LOOP
) ;
582 called by dispatcher & fddi_init() (driver)
588 void pcm(struct s_smc
*smc
, const int np
, int event
)
593 struct fddi_mib_p
*mib
;
597 * ignore 2nd PHY if SAS
599 if ((np
!= PS
) && (smc
->s
.sas
== SMT_SAS
))
604 oldstate
= mib
->fddiPORTPCMState
;
606 DB_PCM("PCM %c: state %s%s, event %s",
608 mib
->fddiPORTPCMState
& AFLAG
? "ACTIONS " : "",
609 pcm_states
[mib
->fddiPORTPCMState
& ~AFLAG
],
611 state
= mib
->fddiPORTPCMState
;
612 pcm_fsm(smc
,phy
,event
) ;
614 } while (state
!= mib
->fddiPORTPCMState
) ;
616 * because the PLC does the bit signaling for us,
617 * we're always in SIGNAL state
618 * the MIB want's to see CONNECT
619 * we therefore fake an entry in the MIB
621 if (state
== PC5_SIGNAL
)
622 mib
->fddiPORTPCMStateX
= PC3_CONNECT
;
624 mib
->fddiPORTPCMStateX
= state
;
630 if ( mib
->fddiPORTPCMState
!= oldstate
&&
631 ((oldstate
== PC8_ACTIVE
) || (mib
->fddiPORTPCMState
== PC8_ACTIVE
))) {
632 smt_srf_event(smc
,SMT_EVENT_PORT_PATH_CHANGE
,
633 (int) (INDEX_PORT
+ phy
->np
),0) ;
638 /* check whether a snmp-trap has to be sent */
640 if ( mib
->fddiPORTPCMState
!= oldstate
) {
641 /* a real state change took place */
642 DB_SNMP ("PCM from %d to %d\n", oldstate
, mib
->fddiPORTPCMState
);
643 if ( mib
->fddiPORTPCMState
== PC0_OFF
) {
644 /* send first trap */
645 snmp_fddi_trap (smc
, 1, (int) mib
->fddiPORTIndex
);
646 } else if ( oldstate
== PC0_OFF
) {
647 /* send second trap */
648 snmp_fddi_trap (smc
, 2, (int) mib
->fddiPORTIndex
);
649 } else if ( mib
->fddiPORTPCMState
!= PC2_TRACE
&&
650 oldstate
== PC8_ACTIVE
) {
651 /* send third trap */
652 snmp_fddi_trap (smc
, 3, (int) mib
->fddiPORTIndex
);
653 } else if ( mib
->fddiPORTPCMState
== PC8_ACTIVE
) {
654 /* send fourth trap */
655 snmp_fddi_trap (smc
, 4, (int) mib
->fddiPORTIndex
);
660 pcm_state_change(smc
,np
,state
) ;
666 static void pcm_fsm(struct s_smc
*smc
, struct s_phy
*phy
, int cmd
)
669 int np
= phy
->np
; /* PHY index */
671 struct fddi_mib_p
*mib
;
673 u_short plc_rev
; /* Revision of the plc */
674 #endif /* nMOT_ELM */
680 * general transitions independent of state
685 if (mib
->fddiPORTPCMState
!= PC9_MAINT
) {
687 AIX_EVENT(smc
, (u_long
) FDDI_RING_STATUS
, (u_long
)
688 FDDI_PORT_EVENT
, (u_long
) FDDI_PORT_STOP
,
689 smt_get_port_event_word(smc
));
694 if (mib
->fddiPORTPCMState
!= PC9_MAINT
)
695 GO_STATE(PC1_BREAK
) ;
699 GO_STATE(PC9_MAINT
) ;
700 AIX_EVENT(smc
, (u_long
) FDDI_RING_STATUS
, (u_long
)
701 FDDI_PORT_EVENT
, (u_long
) FDDI_PORT_DISABLED
,
702 smt_get_port_event_word(smc
));
704 case PC_TIMEOUT_LCT
:
705 /* if long or extended LCT */
706 stop_pcm_timer0(smc
,phy
) ;
707 CLEAR(PLC(np
,PL_CNTRL_B
),PL_LONG
) ;
708 /* end of LCT is indicate by PCM_CODE (initiate PCM event) */
712 switch(mib
->fddiPORTPCMState
) {
713 case ACTIONS(PC0_OFF
) :
714 stop_pcm_timer0(smc
,phy
) ;
715 outpw(PLC(np
,PL_CNTRL_A
),0) ;
716 CLEAR(PLC(np
,PL_CNTRL_B
),PL_PC_JOIN
) ;
717 CLEAR(PLC(np
,PL_CNTRL_B
),PL_LONG
) ;
718 sm_ph_lem_stop(smc
,np
) ; /* disable LEM */
719 phy
->cf_loop
= FALSE
;
720 phy
->cf_join
= FALSE
;
721 queue_event(smc
,EVENT_CFM
,CF_JOIN
+np
) ;
722 plc_go_state(smc
,np
,PL_PCM_STOP
) ;
723 mib
->fddiPORTConnectState
= PCM_DISABLED
;
728 if (cmd
== PC_MAINT
) {
729 GO_STATE(PC9_MAINT
) ;
733 case ACTIONS(PC1_BREAK
) :
734 /* Stop the LCT timer if we came from Signal state */
735 stop_pcm_timer0(smc
,phy
) ;
737 plc_go_state(smc
,np
,0) ;
738 CLEAR(PLC(np
,PL_CNTRL_B
),PL_PC_JOIN
) ;
739 CLEAR(PLC(np
,PL_CNTRL_B
),PL_LONG
) ;
740 sm_ph_lem_stop(smc
,np
) ; /* disable LEM */
742 * if vector is already loaded, go to OFF to clear PCM_SIGNAL
745 if (inpw(PLC(np
,PL_STATUS_B
)) & PL_PCM_SIGNAL
) {
746 plc_go_state(smc
,np
,PL_PCM_STOP
) ;
751 * Go to OFF state in any case.
753 plc_go_state(smc
,np
,PL_PCM_STOP
) ;
755 if (mib
->fddiPORTPC_Withhold
== PC_WH_NONE
)
756 mib
->fddiPORTConnectState
= PCM_CONNECTING
;
757 phy
->cf_loop
= FALSE
;
758 phy
->cf_join
= FALSE
;
759 queue_event(smc
,EVENT_CFM
,CF_JOIN
+np
) ;
760 phy
->ls_flag
= FALSE
;
761 phy
->pc_mode
= PM_NONE
; /* needed by CFM */
762 phy
->bitn
= 0 ; /* bit signaling start bit */
763 for (i
= 0 ; i
< 3 ; i
++)
764 pc_tcode_actions(smc
,i
,phy
) ;
766 /* Set the non-active interrupt mask register */
767 outpw(PLC(np
,PL_INTR_MASK
),plc_imsk_na
) ;
770 * If the LCT was stopped. There might be a
771 * PCM_CODE interrupt event present.
772 * This must be cleared.
774 (void)inpw(PLC(np
,PL_INTR_EVENT
)) ;
776 /* Get the plc revision for revision dependent code */
777 plc_rev
= inpw(PLC(np
,PL_STATUS_A
)) & PLC_REV_MASK
;
779 if (plc_rev
!= PLC_REV_SN3
)
783 * No supernet III PLC, so set Xmit verctor and
784 * length BEFORE starting the state machine.
786 if (plc_send_bits(smc
,phy
,3)) {
792 * Now give the Start command.
793 * - The start command shall be done before setting the bits
794 * to be signaled. (In PLC-S description and PLCS in SN3.
795 * - The start command shall be issued AFTER setting the
796 * XMIT vector and the XMIT length register.
798 * We do it exactly according this specs for the old PLC and
799 * the new PLCS inside the SN3.
800 * For the usual PLCS we try it the way it is done for the
801 * old PLC and set the XMIT registers again, if the PLC is
802 * not in SIGNAL state. This is done according to an PLCS
806 plc_go_state(smc
,np
,PL_PCM_START
) ;
809 * workaround for PLC-S eng. sample errata
812 if (!(inpw(PLC(np
,PL_STATUS_B
)) & PL_PCM_SIGNAL
))
814 if (((inpw(PLC(np
,PL_STATUS_A
)) & PLC_REV_MASK
) !=
816 !(inpw(PLC(np
,PL_STATUS_B
)) & PL_PCM_SIGNAL
))
817 #endif /* nMOT_ELM */
820 * Set register again (PLCS errata) or the first time
823 (void) plc_send_bits(smc
,phy
,3) ;
829 GO_STATE(PC5_SIGNAL
) ;
830 plc
->p_state
= PS_BIT3
;
837 case ACTIONS(PC2_TRACE
) :
838 plc_go_state(smc
,np
,PL_PCM_TRACE
) ;
844 case PC3_CONNECT
: /* these states are done by hardware */
848 case ACTIONS(PC5_SIGNAL
) :
852 if ((cmd
!= PC_SIGNAL
) && (cmd
!= PC_TIMEOUT_LCT
))
854 switch (plc
->p_state
) {
856 for (i
= 0 ; i
<= 2 ; i
++)
857 pc_rcode_actions(smc
,i
,phy
) ;
858 pc_tcode_actions(smc
,3,phy
) ;
859 plc
->p_state
= PS_BIT4
;
863 if (plc_send_bits(smc
,phy
,1)) {
868 pc_rcode_actions(smc
,3,phy
) ;
869 for (i
= 4 ; i
<= 6 ; i
++)
870 pc_tcode_actions(smc
,i
,phy
) ;
871 plc
->p_state
= PS_BIT7
;
875 if (plc_send_bits(smc
,phy
,3)) {
880 for (i
= 3 ; i
<= 6 ; i
++)
881 pc_rcode_actions(smc
,i
,phy
) ;
882 plc
->p_state
= PS_LCT
;
886 sm_ph_lem_start(smc
,np
,(int)smc
->s
.lct_short
) ; /* enable LEM */
888 i
= inpw(PLC(np
,PL_CNTRL_B
)) & ~PL_PC_LOOP
;
889 outpw(PLC(np
,PL_CNTRL_B
),i
) ; /* must be cleared */
890 outpw(PLC(np
,PL_CNTRL_B
),i
| PL_RLBP
) ;
893 /* check for local LCT failure */
894 pc_tcode_actions(smc
,7,phy
) ;
898 plc
->p_state
= PS_BIT8
;
902 if (plc_send_bits(smc
,phy
,1)) {
907 /* check for remote LCT failure */
908 pc_rcode_actions(smc
,7,phy
) ;
909 if (phy
->t_val
[7] || phy
->r_val
[7]) {
910 plc_go_state(smc
,np
,PL_PCM_STOP
) ;
911 GO_STATE(PC1_BREAK
) ;
914 for (i
= 8 ; i
<= 9 ; i
++)
915 pc_tcode_actions(smc
,i
,phy
) ;
916 plc
->p_state
= PS_JOIN
;
920 if (plc_send_bits(smc
,phy
,2)) {
925 for (i
= 8 ; i
<= 9 ; i
++)
926 pc_rcode_actions(smc
,i
,phy
) ;
927 plc
->p_state
= PS_ACTIVE
;
933 case ACTIONS(PC6_JOIN
) :
935 * prevent mux error when going from WRAP_A to WRAP_B
937 if (smc
->s
.sas
== SMT_DAS
&& np
== PB
&&
938 (smc
->y
[PA
].pc_mode
== PM_TREE
||
939 smc
->y
[PB
].pc_mode
== PM_TREE
)) {
940 SETMASK(PLC(np
,PL_CNTRL_A
),
941 PL_SC_REM_LOOP
,PL_SC_REM_LOOP
) ;
942 SETMASK(PLC(np
,PL_CNTRL_B
),
943 PL_CONFIG_CNTRL
,PL_CONFIG_CNTRL
) ;
945 SETMASK(PLC(np
,PL_CNTRL_B
),PL_PC_JOIN
,PL_PC_JOIN
) ;
946 SETMASK(PLC(np
,PL_CNTRL_B
),PL_PC_JOIN
,PL_PC_JOIN
) ;
951 switch (plc
->p_state
) {
955 phy
->cf_join
= TRUE
;
956 queue_event(smc
,EVENT_CFM
,CF_JOIN
+np
) ;
959 GO_STATE(PC8_ACTIVE
) ;
961 if (cmd
== PC_TRACE
) {
962 GO_STATE(PC2_TRACE
) ;
972 case ACTIONS(PC8_ACTIVE
) :
976 sm_ph_lem_start(smc
,(int)phy
->np
,LCT_LEM_MAX
) ;
978 phy
->tr_flag
= FALSE
;
979 mib
->fddiPORTConnectState
= PCM_ACTIVE
;
981 /* Set the active interrupt mask register */
982 outpw(PLC(np
,PL_INTR_MASK
),plc_imsk_act
) ;
987 /*PC81 is done by PL_TNE_EXPIRED irq */
989 if (cmd
== PC_TRACE
) {
990 GO_STATE(PC2_TRACE
) ;
993 /*PC88c: is done by TRACE_PROP irq */
996 case ACTIONS(PC9_MAINT
) :
997 stop_pcm_timer0(smc
,phy
) ;
998 CLEAR(PLC(np
,PL_CNTRL_B
),PL_PC_JOIN
) ;
999 CLEAR(PLC(np
,PL_CNTRL_B
),PL_LONG
) ;
1000 CLEAR(PLC(np
,PL_INTR_MASK
),PL_LE_CTR
) ; /* disable LEM int. */
1001 sm_ph_lem_stop(smc
,np
) ; /* disable LEM */
1002 phy
->cf_loop
= FALSE
;
1003 phy
->cf_join
= FALSE
;
1004 queue_event(smc
,EVENT_CFM
,CF_JOIN
+np
) ;
1005 plc_go_state(smc
,np
,PL_PCM_STOP
) ;
1006 mib
->fddiPORTConnectState
= PCM_DISABLED
;
1007 SETMASK(PLC(np
,PL_CNTRL_B
),PL_MAINT
,PL_MAINT
) ;
1008 sm_ph_linestate(smc
,np
,(int) MIB2LS(mib
->fddiPORTMaint_LS
)) ;
1009 outpw(PLC(np
,PL_CNTRL_A
),PL_SC_BYPASS
) ;
1013 DB_PCMN(1, "PCM %c : MAINT", phy
->phy_name
);
1015 if (cmd
== PC_ENABLE
) {
1022 SMT_PANIC(smc
,SMT_E0118
, SMT_E0118_MSG
) ;
1028 * force line state on a PHY output (only in MAINT state)
1030 static void sm_ph_linestate(struct s_smc
*smc
, int phy
, int ls
)
1036 cntrl
= (inpw(PLC(phy
,PL_CNTRL_B
)) & ~PL_MAINT_LS
) |
1037 PL_PCM_STOP
| PL_MAINT
;
1039 case PC_QLS
: /* Force Quiet */
1040 cntrl
|= PL_M_QUI0
;
1042 case PC_MLS
: /* Force Master */
1043 cntrl
|= PL_M_MASTR
;
1045 case PC_HLS
: /* Force Halt */
1046 cntrl
|= PL_M_HALT
;
1049 case PC_ILS
: /* Force Idle */
1050 cntrl
|= PL_M_IDLE
;
1052 case PC_LS_PDR
: /* Enable repeat filter */
1053 cntrl
|= PL_M_TPDR
;
1056 outpw(PLC(phy
,PL_CNTRL_B
),cntrl
) ;
1059 static void reset_lem_struct(struct s_phy
*phy
)
1061 struct lem_counter
*lem
= &phy
->lem
;
1063 phy
->mib
->fddiPORTLer_Estimate
= 15 ;
1064 lem
->lem_float_ber
= 15 * 100 ;
1068 * link error monitor
1070 static void lem_evaluate(struct s_smc
*smc
, struct s_phy
*phy
)
1074 struct lem_counter
*lem
= &phy
->lem
;
1075 struct fddi_mib_p
*mib
;
1083 errors
= inpw(PLC(((int) phy
->np
),PL_LINK_ERR_CTR
)) ;
1084 lem
->lem_errors
+= errors
;
1085 mib
->fddiPORTLem_Ct
+= errors
;
1087 errors
= lem
->lem_errors
;
1089 * calculation is called on a intervall of 8 seconds
1090 * -> this means, that one error in 8 sec. is one of 8*125*10E6
1091 * the same as BER = 10E-9
1093 * -> 9 errors in 8 seconds mean:
1094 * BER = 9 * 10E-9 and this is
1095 * < 10E-8, so the limit of 10E-8 is not reached!
1098 if (!errors
) ber
= 15 ;
1099 else if (errors
<= 9) ber
= 9 ;
1100 else if (errors
<= 99) ber
= 8 ;
1101 else if (errors
<= 999) ber
= 7 ;
1102 else if (errors
<= 9999) ber
= 6 ;
1103 else if (errors
<= 99999) ber
= 5 ;
1104 else if (errors
<= 999999) ber
= 4 ;
1105 else if (errors
<= 9999999) ber
= 3 ;
1106 else if (errors
<= 99999999) ber
= 2 ;
1107 else if (errors
<= 999999999) ber
= 1 ;
1114 lem
->lem_float_ber
= lem
->lem_float_ber
* 7 + ber
* 3 ;
1115 lem
->lem_float_ber
/= 10 ;
1116 mib
->fddiPORTLer_Estimate
= lem
->lem_float_ber
/ 100 ;
1117 if (mib
->fddiPORTLer_Estimate
< 4) {
1118 mib
->fddiPORTLer_Estimate
= 4 ;
1121 if (lem
->lem_errors
) {
1122 DB_PCMN(1, "LEM %c :", phy
->np
== PB
? 'B' : 'A');
1123 DB_PCMN(1, "errors : %ld", lem
->lem_errors
);
1124 DB_PCMN(1, "sum_errors : %ld", mib
->fddiPORTLem_Ct
);
1125 DB_PCMN(1, "current BER : 10E-%d", ber
/ 100);
1126 DB_PCMN(1, "float BER : 10E-(%d/100)", lem
->lem_float_ber
);
1127 DB_PCMN(1, "avg. BER : 10E-%d", mib
->fddiPORTLer_Estimate
);
1130 lem
->lem_errors
= 0L ;
1133 cond
= (mib
->fddiPORTLer_Estimate
<= mib
->fddiPORTLer_Alarm
) ?
1135 #ifdef SMT_EXT_CUTOFF
1136 smt_ler_alarm_check(smc
,phy
,cond
) ;
1137 #endif /* nSMT_EXT_CUTOFF */
1138 if (cond
!= mib
->fddiPORTLerFlag
) {
1139 smt_srf_event(smc
,SMT_COND_PORT_LER
,
1140 (int) (INDEX_PORT
+ phy
->np
) ,cond
) ;
1144 if ( mib
->fddiPORTLer_Estimate
<= mib
->fddiPORTLer_Cutoff
) {
1145 phy
->pc_lem_fail
= TRUE
; /* flag */
1146 mib
->fddiPORTLem_Reject_Ct
++ ;
1148 * "forgive 10e-2" if we cutoff so we can come
1151 lem
->lem_float_ber
+= 2*100 ;
1155 DB_PCMN(1, "PCM: LER cutoff on port %d cutoff %d",
1156 phy
->np
, mib
->fddiPORTLer_Cutoff
);
1158 #ifdef SMT_EXT_CUTOFF
1159 smt_port_off_event(smc
,phy
->np
);
1160 #else /* nSMT_EXT_CUTOFF */
1161 queue_event(smc
,(int)(EVENT_PCM
+phy
->np
),PC_START
) ;
1162 #endif /* nSMT_EXT_CUTOFF */
1167 * called by SMT to calculate LEM bit error rate
1169 void sm_lem_evaluate(struct s_smc
*smc
)
1173 for (np
= 0 ; np
< NUMPHYS
; np
++)
1174 lem_evaluate(smc
,&smc
->y
[np
]) ;
1177 static void lem_check_lct(struct s_smc
*smc
, struct s_phy
*phy
)
1179 struct lem_counter
*lem
= &phy
->lem
;
1180 struct fddi_mib_p
*mib
;
1185 phy
->pc_lem_fail
= FALSE
; /* flag */
1186 errors
= inpw(PLC(((int)phy
->np
),PL_LINK_ERR_CTR
)) ;
1187 lem
->lem_errors
+= errors
;
1188 mib
->fddiPORTLem_Ct
+= errors
;
1189 if (lem
->lem_errors
) {
1190 switch(phy
->lc_test
) {
1192 if (lem
->lem_errors
>= smc
->s
.lct_short
)
1193 phy
->pc_lem_fail
= TRUE
;
1196 if (lem
->lem_errors
>= smc
->s
.lct_medium
)
1197 phy
->pc_lem_fail
= TRUE
;
1200 if (lem
->lem_errors
>= smc
->s
.lct_long
)
1201 phy
->pc_lem_fail
= TRUE
;
1204 if (lem
->lem_errors
>= smc
->s
.lct_extended
)
1205 phy
->pc_lem_fail
= TRUE
;
1208 DB_PCMN(1, " >>errors : %lu", lem
->lem_errors
);
1210 if (phy
->pc_lem_fail
) {
1211 mib
->fddiPORTLCTFail_Ct
++ ;
1212 mib
->fddiPORTLem_Reject_Ct
++ ;
1215 mib
->fddiPORTLCTFail_Ct
= 0 ;
1221 static void sm_ph_lem_start(struct s_smc
*smc
, int np
, int threshold
)
1223 struct lem_counter
*lem
= &smc
->y
[np
].lem
;
1226 lem
->lem_errors
= 0L ;
1228 /* Do NOT reset mib->fddiPORTLer_Estimate here. It is called too
1232 outpw(PLC(np
,PL_LE_THRESHOLD
),threshold
) ;
1233 (void)inpw(PLC(np
,PL_LINK_ERR_CTR
)) ; /* clear error counter */
1236 SETMASK(PLC(np
,PL_INTR_MASK
),PL_LE_CTR
,PL_LE_CTR
) ;
1239 static void sm_ph_lem_stop(struct s_smc
*smc
, int np
)
1241 struct lem_counter
*lem
= &smc
->y
[np
].lem
;
1244 CLEAR(PLC(np
,PL_INTR_MASK
),PL_LE_CTR
) ;
1249 * receive actions are called AFTER the bit n is received,
1250 * i.e. if pc_rcode_actions(5) is called, bit 6 is the next bit to be received
1254 * PCM pseudo code 5.1 .. 6.1
1256 static void pc_rcode_actions(struct s_smc
*smc
, int bit
, struct s_phy
*phy
)
1258 struct fddi_mib_p
*mib
;
1262 DB_PCMN(1, "SIG rec %x %x:", bit
, phy
->r_val
[bit
]);
1271 if (phy
->r_val
[1] == 0 && phy
->r_val
[2] == 0)
1272 mib
->fddiPORTNeighborType
= TA
;
1273 else if (phy
->r_val
[1] == 0 && phy
->r_val
[2] == 1)
1274 mib
->fddiPORTNeighborType
= TB
;
1275 else if (phy
->r_val
[1] == 1 && phy
->r_val
[2] == 0)
1276 mib
->fddiPORTNeighborType
= TS
;
1277 else if (phy
->r_val
[1] == 1 && phy
->r_val
[2] == 1)
1278 mib
->fddiPORTNeighborType
= TM
;
1281 if (mib
->fddiPORTMy_Type
== TM
&&
1282 mib
->fddiPORTNeighborType
== TM
) {
1283 DB_PCMN(1, "PCM %c : E100 withhold M-M",
1285 mib
->fddiPORTPC_Withhold
= PC_WH_M_M
;
1286 RS_SET(smc
,RS_EVENT
) ;
1288 else if (phy
->t_val
[3] || phy
->r_val
[3]) {
1289 mib
->fddiPORTPC_Withhold
= PC_WH_NONE
;
1290 if (mib
->fddiPORTMy_Type
== TM
||
1291 mib
->fddiPORTNeighborType
== TM
)
1292 phy
->pc_mode
= PM_TREE
;
1294 phy
->pc_mode
= PM_PEER
;
1296 /* reevaluate the selection criteria (wc_flag) */
1297 all_selection_criteria (smc
);
1300 mib
->fddiPORTPC_Withhold
= PC_WH_PATH
;
1304 mib
->fddiPORTPC_Withhold
= PC_WH_OTHER
;
1305 RS_SET(smc
,RS_EVENT
) ;
1306 DB_PCMN(1, "PCM %c : E101 withhold other",
1309 phy
->twisted
= ((mib
->fddiPORTMy_Type
!= TS
) &&
1310 (mib
->fddiPORTMy_Type
!= TM
) &&
1311 (mib
->fddiPORTNeighborType
==
1312 mib
->fddiPORTMy_Type
)) ;
1314 DB_PCMN(1, "PCM %c : E102 !!! TWISTED !!!",
1321 if (phy
->t_val
[4] || phy
->r_val
[4]) {
1322 if ((phy
->t_val
[4] && phy
->t_val
[5]) ||
1323 (phy
->r_val
[4] && phy
->r_val
[5]) )
1324 phy
->lc_test
= LC_EXTENDED
;
1326 phy
->lc_test
= LC_LONG
;
1328 else if (phy
->t_val
[5] || phy
->r_val
[5])
1329 phy
->lc_test
= LC_MEDIUM
;
1331 phy
->lc_test
= LC_SHORT
;
1332 switch (phy
->lc_test
) {
1333 case LC_SHORT
: /* 50ms */
1334 outpw(PLC((int)phy
->np
,PL_LC_LENGTH
), TP_LC_LENGTH
) ;
1335 phy
->t_next
[7] = smc
->s
.pcm_lc_short
;
1337 case LC_MEDIUM
: /* 500ms */
1338 outpw(PLC((int)phy
->np
,PL_LC_LENGTH
), TP_LC_LONGLN
) ;
1339 phy
->t_next
[7] = smc
->s
.pcm_lc_medium
;
1342 SETMASK(PLC((int)phy
->np
,PL_CNTRL_B
),PL_LONG
,PL_LONG
) ;
1343 phy
->t_next
[7] = smc
->s
.pcm_lc_long
;
1346 SETMASK(PLC((int)phy
->np
,PL_CNTRL_B
),PL_LONG
,PL_LONG
) ;
1347 phy
->t_next
[7] = smc
->s
.pcm_lc_extended
;
1350 if (phy
->t_next
[7] > smc
->s
.pcm_lc_medium
) {
1351 start_pcm_timer0(smc
,phy
->t_next
[7],PC_TIMEOUT_LCT
,phy
);
1353 DB_PCMN(1, "LCT timer = %ld us", phy
->t_next
[7]);
1354 phy
->t_next
[9] = smc
->s
.pcm_t_next_9
;
1357 if (phy
->t_val
[6]) {
1358 phy
->cf_loop
= TRUE
;
1360 phy
->td_flag
= TRUE
;
1363 if (phy
->t_val
[7] || phy
->r_val
[7]) {
1364 DB_PCMN(1, "PCM %c : E103 LCT fail %s",
1366 phy
->t_val
[7] ? "local" : "remote");
1367 queue_event(smc
,(int)(EVENT_PCM
+phy
->np
),PC_START
) ;
1371 if (phy
->t_val
[8] || phy
->r_val
[8]) {
1373 phy
->cf_loop
= TRUE
;
1374 phy
->td_flag
= TRUE
;
1378 if (phy
->r_val
[9]) {
1379 /* neighbor intends to have MAC on output */ ;
1380 mib
->fddiPORTMacIndicated
.R_val
= TRUE
;
1383 /* neighbor does not intend to have MAC on output */ ;
1384 mib
->fddiPORTMacIndicated
.R_val
= FALSE
;
1391 * PCM pseudo code 5.1 .. 6.1
1393 static void pc_tcode_actions(struct s_smc
*smc
, const int bit
, struct s_phy
*phy
)
1396 struct fddi_mib_p
*mib
;
1402 phy
->t_val
[0] = 0 ; /* no escape used */
1405 if (mib
->fddiPORTMy_Type
== TS
|| mib
->fddiPORTMy_Type
== TM
)
1411 if (mib
->fddiPORTMy_Type
== TB
|| mib
->fddiPORTMy_Type
== TM
)
1421 type
= mib
->fddiPORTMy_Type
;
1422 ne
= mib
->fddiPORTNeighborType
;
1423 policy
= smc
->mib
.fddiSMTConnectionPolicy
;
1425 phy
->t_val
[3] = 1 ; /* Accept connection */
1429 ((policy
& POLICY_AA
) && ne
== TA
) ||
1430 ((policy
& POLICY_AB
) && ne
== TB
) ||
1431 ((policy
& POLICY_AS
) && ne
== TS
) ||
1432 ((policy
& POLICY_AM
) && ne
== TM
) )
1433 phy
->t_val
[3] = 0 ; /* Reject */
1437 ((policy
& POLICY_BA
) && ne
== TA
) ||
1438 ((policy
& POLICY_BB
) && ne
== TB
) ||
1439 ((policy
& POLICY_BS
) && ne
== TS
) ||
1440 ((policy
& POLICY_BM
) && ne
== TM
) )
1441 phy
->t_val
[3] = 0 ; /* Reject */
1445 ((policy
& POLICY_SA
) && ne
== TA
) ||
1446 ((policy
& POLICY_SB
) && ne
== TB
) ||
1447 ((policy
& POLICY_SS
) && ne
== TS
) ||
1448 ((policy
& POLICY_SM
) && ne
== TM
) )
1449 phy
->t_val
[3] = 0 ; /* Reject */
1453 ((policy
& POLICY_MA
) && ne
== TA
) ||
1454 ((policy
& POLICY_MB
) && ne
== TB
) ||
1455 ((policy
& POLICY_MS
) && ne
== TS
) ||
1456 ((policy
& POLICY_MM
) && ne
== TM
) )
1457 phy
->t_val
[3] = 0 ; /* Reject */
1462 * detect undesirable connection attempt event
1464 if ( (type
== TA
&& ne
== TA
) ||
1465 (type
== TA
&& ne
== TS
) ||
1466 (type
== TB
&& ne
== TB
) ||
1467 (type
== TB
&& ne
== TS
) ||
1468 (type
== TS
&& ne
== TA
) ||
1469 (type
== TS
&& ne
== TB
) ) {
1470 smt_srf_event(smc
,SMT_EVENT_PORT_CONNECTION
,
1471 (int) (INDEX_PORT
+ phy
->np
) ,0) ;
1477 if (mib
->fddiPORTPC_Withhold
== PC_WH_NONE
) {
1478 if (phy
->pc_lem_fail
) {
1479 phy
->t_val
[4] = 1 ; /* long */
1484 if (mib
->fddiPORTLCTFail_Ct
> 0)
1485 phy
->t_val
[5] = 1 ; /* medium */
1487 phy
->t_val
[5] = 0 ; /* short */
1490 * Implementers choice: use medium
1491 * instead of short when undesired
1492 * connection attempt is made.
1495 phy
->t_val
[5] = 1 ; /* medium */
1497 mib
->fddiPORTConnectState
= PCM_CONNECTING
;
1500 mib
->fddiPORTConnectState
= PCM_STANDBY
;
1501 phy
->t_val
[4] = 1 ; /* extended */
1508 /* we do NOT have a MAC for LCT */
1512 phy
->cf_loop
= FALSE
;
1513 lem_check_lct(smc
,phy
) ;
1514 if (phy
->pc_lem_fail
) {
1515 DB_PCMN(1, "PCM %c : E104 LCT failed", phy
->phy_name
);
1522 phy
->t_val
[8] = 0 ; /* Don't request MAC loopback */
1526 if ((mib
->fddiPORTPC_Withhold
!= PC_WH_NONE
) ||
1527 ((smc
->s
.sas
== SMT_DAS
) && (phy
->wc_flag
))) {
1528 queue_event(smc
,EVENT_PCM
+np
,PC_START
) ;
1531 phy
->t_val
[9] = FALSE
;
1532 switch (smc
->s
.sas
) {
1535 * MAC intended on output
1537 if (phy
->pc_mode
== PM_TREE
) {
1538 if ((np
== PB
) || ((np
== PA
) &&
1539 (smc
->y
[PB
].mib
->fddiPORTConnectState
!=
1541 phy
->t_val
[9] = TRUE
;
1545 phy
->t_val
[9] = TRUE
;
1550 phy
->t_val
[9] = TRUE
;
1555 * MAC intended on output
1558 phy
->t_val
[9] = TRUE
;
1562 mib
->fddiPORTMacIndicated
.T_val
= phy
->t_val
[9] ;
1565 DB_PCMN(1, "SIG snd %x %x:", bit
, phy
->t_val
[bit
]);
1569 * return status twisted (called by SMT)
1571 int pcm_status_twisted(struct s_smc
*smc
)
1574 if (smc
->s
.sas
!= SMT_DAS
)
1576 if (smc
->y
[PA
].twisted
&& (smc
->y
[PA
].mib
->fddiPORTPCMState
== PC8_ACTIVE
))
1578 if (smc
->y
[PB
].twisted
&& (smc
->y
[PB
].mib
->fddiPORTPCMState
== PC8_ACTIVE
))
1584 * return status (called by SMT)
1590 void pcm_status_state(struct s_smc
*smc
, int np
, int *type
, int *state
,
1591 int *remote
, int *mac
)
1593 struct s_phy
*phy
= &smc
->y
[np
] ;
1594 struct fddi_mib_p
*mib
;
1598 /* remote PHY type and MAC - set only if active */
1600 *type
= mib
->fddiPORTMy_Type
; /* our PHY type */
1601 *state
= mib
->fddiPORTConnectState
;
1602 *remote
= mib
->fddiPORTNeighborType
;
1604 switch(mib
->fddiPORTPCMState
) {
1606 *mac
= mib
->fddiPORTMacIndicated
.R_val
;
1612 * return rooted station status (called by SMT)
1614 int pcm_rooted_station(struct s_smc
*smc
)
1618 for (n
= 0 ; n
< NUMPHYS
; n
++) {
1619 if (smc
->y
[n
].mib
->fddiPORTPCMState
== PC8_ACTIVE
&&
1620 smc
->y
[n
].mib
->fddiPORTNeighborType
== TM
)
1627 * Interrupt actions for PLC & PCM events
1629 void plc_irq(struct s_smc
*smc
, int np
, unsigned int cmd
)
1630 /* int np; PHY index */
1632 struct s_phy
*phy
= &smc
->y
[np
] ;
1633 struct s_plc
*plc
= &phy
->plc
;
1637 #endif /* SUPERNET_3 */
1640 if (np
>= smc
->s
.numphys
) {
1644 if (cmd
& PL_EBUF_ERR
) { /* elastic buff. det. over-|underflow*/
1646 * Check whether the SRF Condition occurred.
1648 if (!plc
->ebuf_cont
&& phy
->mib
->fddiPORTPCMState
== PC8_ACTIVE
){
1650 * This is the real Elasticity Error.
1651 * More than one in a row are treated as a
1653 * Only count this in the active state.
1655 phy
->mib
->fddiPORTEBError_Ct
++ ;
1660 if (plc
->ebuf_cont
<= 1000) {
1662 * Prevent counter from being wrapped after
1663 * hanging years in that interrupt.
1665 plc
->ebuf_cont
++ ; /* Ebuf continuous error */
1669 if (plc
->ebuf_cont
== 1000 &&
1670 ((inpw(PLC(np
,PL_STATUS_A
)) & PLC_REV_MASK
) ==
1673 * This interrupt remeained high for at least
1674 * 1000 consecutive interrupt calls.
1676 * This is caused by a hardware error of the
1677 * ORION part of the Supernet III chipset.
1679 * Disable this bit from the mask.
1681 corr_mask
= (plc_imsk_na
& ~PL_EBUF_ERR
) ;
1682 outpw(PLC(np
,PL_INTR_MASK
),corr_mask
);
1685 * Disconnect from the ring.
1686 * Call the driver with the reset indication.
1688 queue_event(smc
,EVENT_ECM
,EC_DISCONNECT
) ;
1691 * Make an error log entry.
1693 SMT_ERR_LOG(smc
,SMT_E0136
, SMT_E0136_MSG
) ;
1696 * Indicate the Reset.
1698 drv_reset_indication(smc
) ;
1700 #endif /* SUPERNET_3 */
1702 /* Reset the continuous error variable */
1703 plc
->ebuf_cont
= 0 ; /* reset Ebuf continuous error */
1705 if (cmd
& PL_PHYINV
) { /* physical layer invalid signal */
1708 if (cmd
& PL_VSYM_CTR
) { /* violation symbol counter has incr.*/
1711 if (cmd
& PL_MINI_CTR
) { /* dep. on PLC_CNTRL_A's MINI_CTR_INT*/
1714 if (cmd
& PL_LE_CTR
) { /* link error event counter */
1718 * note: PL_LINK_ERR_CTR MUST be read to clear it
1720 j
= inpw(PLC(np
,PL_LE_THRESHOLD
)) ;
1721 i
= inpw(PLC(np
,PL_LINK_ERR_CTR
)) ;
1724 /* wrapped around */
1728 if (phy
->lem
.lem_on
) {
1729 /* Note: Lem errors shall only be counted when
1730 * link is ACTIVE or LCT is active.
1732 phy
->lem
.lem_errors
+= i
;
1733 phy
->mib
->fddiPORTLem_Ct
+= i
;
1736 if (cmd
& PL_TPC_EXPIRED
) { /* TPC timer reached zero */
1737 if (plc
->p_state
== PS_LCT
) {
1745 if (cmd
& PL_LS_MATCH
) { /* LS == LS in PLC_CNTRL_B's MATCH_LS*/
1746 switch (inpw(PLC(np
,PL_CNTRL_B
)) & PL_MATCH_LS
) {
1747 case PL_I_IDLE
: phy
->curr_ls
= PC_ILS
; break ;
1748 case PL_I_HALT
: phy
->curr_ls
= PC_HLS
; break ;
1749 case PL_I_MASTR
: phy
->curr_ls
= PC_MLS
; break ;
1750 case PL_I_QUIET
: phy
->curr_ls
= PC_QLS
; break ;
1753 if (cmd
& PL_PCM_BREAK
) { /* PCM has entered the BREAK state */
1756 reason
= inpw(PLC(np
,PL_STATUS_B
)) & PL_BREAK_REASON
;
1759 case PL_B_PCS
: plc
->b_pcs
++ ; break ;
1760 case PL_B_TPC
: plc
->b_tpc
++ ; break ;
1761 case PL_B_TNE
: plc
->b_tne
++ ; break ;
1762 case PL_B_QLS
: plc
->b_qls
++ ; break ;
1763 case PL_B_ILS
: plc
->b_ils
++ ; break ;
1764 case PL_B_HLS
: plc
->b_hls
++ ; break ;
1767 /*jd 05-Aug-1999 changed: Bug #10419 */
1768 DB_PCMN(1, "PLC %d: MDcF = %x", np
, smc
->e
.DisconnectFlag
);
1769 if (smc
->e
.DisconnectFlag
== FALSE
) {
1770 DB_PCMN(1, "PLC %d: restart (reason %x)", np
, reason
);
1771 queue_event(smc
,EVENT_PCM
+np
,PC_START
) ;
1774 DB_PCMN(1, "PLC %d: NO!! restart (reason %x)",
1780 * If both CODE & ENABLE are set ignore enable
1782 if (cmd
& PL_PCM_CODE
) { /* receive last sign.-bit | LCT complete */
1783 queue_event(smc
,EVENT_PCM
+np
,PC_SIGNAL
) ;
1784 n
= inpw(PLC(np
,PL_RCV_VECTOR
)) ;
1785 for (i
= 0 ; i
< plc
->p_bits
; i
++) {
1786 phy
->r_val
[plc
->p_start
+i
] = n
& 1 ;
1790 else if (cmd
& PL_PCM_ENABLED
) { /* asserted SC_JOIN, scrub.completed*/
1791 queue_event(smc
,EVENT_PCM
+np
,PC_JOIN
) ;
1793 if (cmd
& PL_TRACE_PROP
) { /* MLS while PC8_ACTIV || PC2_TRACE */
1795 if (!phy
->tr_flag
) {
1796 DB_PCMN(1, "PCM : irq TRACE_PROP %d %d",
1797 np
, smc
->mib
.fddiSMTECMState
);
1798 phy
->tr_flag
= TRUE
;
1799 smc
->e
.trace_prop
|= ENTITY_BIT(ENTITY_PHY(np
)) ;
1800 queue_event(smc
,EVENT_ECM
,EC_TRACE_PROP
) ;
1804 * filter PLC glitch ???
1805 * QLS || HLS only while in PC2_TRACE state
1807 if ((cmd
& PL_SELF_TEST
) && (phy
->mib
->fddiPORTPCMState
== PC2_TRACE
)) {
1809 if (smc
->e
.path_test
== PT_PASSED
) {
1810 DB_PCMN(1, "PCM : state = %s %d",
1811 get_pcmstate(smc
, np
),
1812 phy
->mib
->fddiPORTPCMState
);
1814 smc
->e
.path_test
= PT_PENDING
;
1815 queue_event(smc
,EVENT_ECM
,EC_PATH_TEST
) ;
1818 if (cmd
& PL_TNE_EXPIRED
) { /* TNE: length of noise events */
1819 /* break_required (TNE > NS_Max) */
1820 if (phy
->mib
->fddiPORTPCMState
== PC8_ACTIVE
) {
1821 if (!phy
->tr_flag
) {
1822 DB_PCMN(1, "PCM %c : PC81 %s",
1823 phy
->phy_name
, "NSE");
1824 queue_event(smc
, EVENT_PCM
+ np
, PC_START
);
1830 if (cmd
& PL_NP_ERR
) { /* NP has requested to r/w an inv reg*/
1836 /* pin inactiv (GND) */
1837 if (cmd
& PL_PARITY_ERR
) { /* p. error dedected on TX9-0 inp */
1840 if (cmd
& PL_LSDO
) { /* carrier detected */
1850 void pcm_get_state(struct s_smc
*smc
, struct smt_state
*state
)
1853 struct pcm_state
*pcs
;
1858 struct fddi_mib_p
*mib
;
1860 for (i
= 0, phy
= smc
->y
, pcs
= state
->pcm_state
; i
< NUMPHYS
;
1861 i
++ , phy
++, pcs
++ ) {
1863 pcs
->pcm_type
= (u_char
) mib
->fddiPORTMy_Type
;
1864 pcs
->pcm_state
= (u_char
) mib
->fddiPORTPCMState
;
1865 pcs
->pcm_mode
= phy
->pc_mode
;
1866 pcs
->pcm_neighbor
= (u_char
) mib
->fddiPORTNeighborType
;
1867 pcs
->pcm_bsf
= mib
->fddiPORTBS_Flag
;
1868 pcs
->pcm_lsf
= phy
->ls_flag
;
1869 pcs
->pcm_lct_fail
= (u_char
) mib
->fddiPORTLCTFail_Ct
;
1870 pcs
->pcm_ls_rx
= LS2MIB(sm_pm_get_ls(smc
,i
)) ;
1871 for (ii
= 0, rbits
= tbits
= 0 ; ii
< NUMBITS
; ii
++) {
1874 if (phy
->r_val
[NUMBITS
-1-ii
])
1876 if (phy
->t_val
[NUMBITS
-1-ii
])
1879 pcs
->pcm_r_val
= rbits
;
1880 pcs
->pcm_t_val
= tbits
;
1884 int get_pcm_state(struct s_smc
*smc
, int np
)
1890 switch (inpw(PLC(np
,PL_STATUS_B
)) & PL_PCM_STATE
) {
1891 case PL_PC0
: pcs
= PC_STOP
; break ;
1892 case PL_PC1
: pcs
= PC_START
; break ;
1893 case PL_PC2
: pcs
= PC_TRACE
; break ;
1894 case PL_PC3
: pcs
= PC_SIGNAL
; break ;
1895 case PL_PC4
: pcs
= PC_SIGNAL
; break ;
1896 case PL_PC5
: pcs
= PC_SIGNAL
; break ;
1897 case PL_PC6
: pcs
= PC_JOIN
; break ;
1898 case PL_PC7
: pcs
= PC_JOIN
; break ;
1899 case PL_PC8
: pcs
= PC_ENABLE
; break ;
1900 case PL_PC9
: pcs
= PC_MAINT
; break ;
1901 default : pcs
= PC_DISABLE
; break ;
1906 char *get_linestate(struct s_smc
*smc
, int np
)
1912 switch (inpw(PLC(np
,PL_STATUS_A
)) & PL_LINE_ST
) {
1913 case PL_L_NLS
: ls
= "NOISE" ; break ;
1914 case PL_L_ALS
: ls
= "ACTIV" ; break ;
1915 case PL_L_UND
: ls
= "UNDEF" ; break ;
1916 case PL_L_ILS4
: ls
= "ILS 4" ; break ;
1917 case PL_L_QLS
: ls
= "QLS" ; break ;
1918 case PL_L_MLS
: ls
= "MLS" ; break ;
1919 case PL_L_HLS
: ls
= "HLS" ; break ;
1920 case PL_L_ILS16
:ls
= "ILS16" ; break ;
1922 default: ls
= "unknown" ; break ;
1928 char *get_pcmstate(struct s_smc
*smc
, int np
)
1934 switch (inpw(PLC(np
,PL_STATUS_B
)) & PL_PCM_STATE
) {
1935 case PL_PC0
: pcs
= "OFF" ; break ;
1936 case PL_PC1
: pcs
= "BREAK" ; break ;
1937 case PL_PC2
: pcs
= "TRACE" ; break ;
1938 case PL_PC3
: pcs
= "CONNECT"; break ;
1939 case PL_PC4
: pcs
= "NEXT" ; break ;
1940 case PL_PC5
: pcs
= "SIGNAL" ; break ;
1941 case PL_PC6
: pcs
= "JOIN" ; break ;
1942 case PL_PC7
: pcs
= "VERIFY" ; break ;
1943 case PL_PC8
: pcs
= "ACTIV" ; break ;
1944 case PL_PC9
: pcs
= "MAINT" ; break ;
1945 default : pcs
= "UNKNOWN" ; break ;
1950 void list_phy(struct s_smc
*smc
)
1955 for (np
= 0 ; np
< NUMPHYS
; np
++) {
1956 plc
= &smc
->y
[np
].plc
;
1957 printf("PHY %d:\tERRORS\t\t\tBREAK_REASONS\t\tSTATES:\n",np
) ;
1958 printf("\tsoft_error: %ld \t\tPC_Start : %ld\n",
1959 plc
->soft_err
,plc
->b_pcs
);
1960 printf("\tparity_err: %ld \t\tTPC exp. : %ld\t\tLine: %s\n",
1961 plc
->parity_err
,plc
->b_tpc
,get_linestate(smc
,np
)) ;
1962 printf("\tebuf_error: %ld \t\tTNE exp. : %ld\n",
1963 plc
->ebuf_err
,plc
->b_tne
) ;
1964 printf("\tphyinvalid: %ld \t\tQLS det. : %ld\t\tPCM : %s\n",
1965 plc
->phyinv
,plc
->b_qls
,get_pcmstate(smc
,np
)) ;
1966 printf("\tviosym_ctr: %ld \t\tILS det. : %ld\n",
1967 plc
->vsym_ctr
,plc
->b_ils
) ;
1968 printf("\tmingap_ctr: %ld \t\tHLS det. : %ld\n",
1969 plc
->mini_ctr
,plc
->b_hls
) ;
1970 printf("\tnodepr_err: %ld\n",plc
->np_err
) ;
1971 printf("\tTPC_exp : %ld\n",plc
->tpc_exp
) ;
1972 printf("\tLEM_err : %ld\n",smc
->y
[np
].lem
.lem_errors
) ;
1978 void pcm_lem_dump(struct s_smc
*smc
)
1982 struct fddi_mib_p
*mib
;
1984 char *entostring() ;
1986 printf("PHY errors BER\n") ;
1987 printf("----------------------\n") ;
1988 for (i
= 0,phy
= smc
->y
; i
< NUMPHYS
; i
++,phy
++) {
1989 if (!plc_is_installed(smc
,i
))
1992 printf("%s\t%ld\t10E-%d\n",
1993 entostring(smc
,ENTITY_PHY(i
)),
1994 mib
->fddiPORTLem_Ct
,
1995 mib
->fddiPORTLer_Estimate
) ;