Merge git://git.kernel.org/pub/scm/linux/kernel/git/wim/linux-2.6-watchdog
[linux-2.6] / drivers / net / skfp / ecm.c
1 /******************************************************************************
2  *
3  *      (C)Copyright 1998,1999 SysKonnect,
4  *      a business unit of Schneider & Koch & Co. Datensysteme GmbH.
5  *
6  *      See the file "skfddi.c" for further information.
7  *
8  *      This program is free software; you can redistribute it and/or modify
9  *      it under the terms of the GNU General Public License as published by
10  *      the Free Software Foundation; either version 2 of the License, or
11  *      (at your option) any later version.
12  *
13  *      The information in this file is provided "AS IS" without warranty.
14  *
15  ******************************************************************************/
16
17 /*
18         SMT ECM
19         Entity Coordination Management
20         Hardware independent state machine
21 */
22
23 /*
24  * Hardware independent state machine implemantation
25  * The following external SMT functions are referenced :
26  *
27  *              queue_event()
28  *              smt_timer_start()
29  *              smt_timer_stop()
30  *
31  *      The following external HW dependent functions are referenced :
32  *              sm_pm_bypass_req()
33  *              sm_pm_ls_latch()
34  *              sm_pm_get_ls()
35  * 
36  *      The following HW dependent events are required :
37  *              NONE
38  *
39  */
40
41 #include "h/types.h"
42 #include "h/fddi.h"
43 #include "h/smc.h"
44
45 #define KERNEL
46 #include "h/smtstate.h"
47
48 #ifndef lint
49 static const char ID_sccs[] = "@(#)ecm.c        2.7 99/08/05 (C) SK " ;
50 #endif
51
52 /*
53  * FSM Macros
54  */
55 #define AFLAG   0x10
56 #define GO_STATE(x)     (smc->mib.fddiSMTECMState = (x)|AFLAG)
57 #define ACTIONS_DONE()  (smc->mib.fddiSMTECMState &= ~AFLAG)
58 #define ACTIONS(x)      (x|AFLAG)
59
60 #define EC0_OUT         0                       /* not inserted */
61 #define EC1_IN          1                       /* inserted */
62 #define EC2_TRACE       2                       /* tracing */
63 #define EC3_LEAVE       3                       /* leaving the ring */
64 #define EC4_PATH_TEST   4                       /* performing path test */
65 #define EC5_INSERT      5                       /* bypass being turned on */
66 #define EC6_CHECK       6                       /* checking bypass */
67 #define EC7_DEINSERT    7                       /* bypass being turnde off */
68
69 #ifdef  DEBUG
70 /*
71  * symbolic state names
72  */
73 static const char * const ecm_states[] = {
74         "EC0_OUT","EC1_IN","EC2_TRACE","EC3_LEAVE","EC4_PATH_TEST",
75         "EC5_INSERT","EC6_CHECK","EC7_DEINSERT"
76 } ;
77
78 /*
79  * symbolic event names
80  */
81 static const char * const ecm_events[] = {
82         "NONE","EC_CONNECT","EC_DISCONNECT","EC_TRACE_PROP","EC_PATH_TEST",
83         "EC_TIMEOUT_TD","EC_TIMEOUT_TMAX",
84         "EC_TIMEOUT_IMAX","EC_TIMEOUT_INMAX","EC_TEST_DONE"
85 } ;
86 #endif
87
88 /*
89  * all Globals  are defined in smc.h
90  * struct s_ecm
91  */
92
93 /*
94  * function declarations
95  */
96
97 static void ecm_fsm(struct s_smc *smc, int cmd);
98 static void start_ecm_timer(struct s_smc *smc, u_long value, int event);
99 static void stop_ecm_timer(struct s_smc *smc);
100 static void prop_actions(struct s_smc *smc);
101
102 /*
103         init ECM state machine
104         clear all ECM vars and flags
105 */
106 void ecm_init(struct s_smc *smc)
107 {
108         smc->e.path_test = PT_PASSED ;
109         smc->e.trace_prop = 0 ;
110         smc->e.sb_flag = 0 ;
111         smc->mib.fddiSMTECMState = ACTIONS(EC0_OUT) ;
112         smc->e.ecm_line_state = FALSE ;
113 }
114
115 /*
116         ECM state machine
117         called by dispatcher
118
119         do
120                 display state change
121                 process event
122         until SM is stable
123 */
124 void ecm(struct s_smc *smc, int event)
125 {
126         int     state ;
127
128         do {
129                 DB_ECM("ECM : state %s%s",
130                         (smc->mib.fddiSMTECMState & AFLAG) ? "ACTIONS " : "",
131                         ecm_states[smc->mib.fddiSMTECMState & ~AFLAG]) ;
132                 DB_ECM(" event %s\n",ecm_events[event],0) ;
133                 state = smc->mib.fddiSMTECMState ;
134                 ecm_fsm(smc,event) ;
135                 event = 0 ;
136         } while (state != smc->mib.fddiSMTECMState) ;
137         ecm_state_change(smc,(int)smc->mib.fddiSMTECMState) ;
138 }
139
140 /*
141         process ECM event
142 */
143 static void ecm_fsm(struct s_smc *smc, int cmd)
144 {
145         int ls_a ;                      /* current line state PHY A */
146         int ls_b ;                      /* current line state PHY B */
147         int     p ;                     /* ports */
148
149
150         smc->mib.fddiSMTBypassPresent = sm_pm_bypass_present(smc) ;
151         if (cmd == EC_CONNECT)
152                 smc->mib.fddiSMTRemoteDisconnectFlag = FALSE ;
153
154         /* For AIX event notification: */
155         /* Is a disconnect  command remotely issued ? */
156         if (cmd == EC_DISCONNECT &&
157                 smc->mib.fddiSMTRemoteDisconnectFlag == TRUE)
158                 AIX_EVENT (smc, (u_long) CIO_HARD_FAIL, (u_long)
159                         FDDI_REMOTE_DISCONNECT, smt_get_event_word(smc),
160                         smt_get_error_word(smc) );
161
162         /*jd 05-Aug-1999 Bug #10419 "Port Disconnect fails at Dup MAc Cond."*/
163         if (cmd == EC_CONNECT) {
164                 smc->e.DisconnectFlag = FALSE ;
165         }
166         else if (cmd == EC_DISCONNECT) {
167                 smc->e.DisconnectFlag = TRUE ;
168         }
169         
170         switch(smc->mib.fddiSMTECMState) {
171         case ACTIONS(EC0_OUT) :
172                 /*
173                  * We do not perform a path test
174                  */
175                 smc->e.path_test = PT_PASSED ;
176                 smc->e.ecm_line_state = FALSE ;
177                 stop_ecm_timer(smc) ;
178                 ACTIONS_DONE() ;
179                 break ;
180         case EC0_OUT:
181                 /*EC01*/
182                 if (cmd == EC_CONNECT && !smc->mib.fddiSMTBypassPresent
183                         && smc->e.path_test==PT_PASSED) {
184                         GO_STATE(EC1_IN) ;
185                         break ;
186                 }
187                 /*EC05*/
188                 else if (cmd == EC_CONNECT && (smc->e.path_test==PT_PASSED) &&
189                         smc->mib.fddiSMTBypassPresent &&
190                         (smc->s.sas == SMT_DAS)) {
191                         GO_STATE(EC5_INSERT) ;
192                         break ;
193                 }
194                 break;
195         case ACTIONS(EC1_IN) :
196                 stop_ecm_timer(smc) ;
197                 smc->e.trace_prop = 0 ;
198                 sm_ma_control(smc,MA_TREQ) ;
199                 for (p = 0 ; p < NUMPHYS ; p++)
200                         if (smc->mib.p[p].fddiPORTHardwarePresent)
201                                 queue_event(smc,EVENT_PCMA+p,PC_START) ;
202                 ACTIONS_DONE() ;
203                 break ;
204         case EC1_IN:
205                 /*EC12*/
206                 if (cmd == EC_TRACE_PROP) {
207                         prop_actions(smc) ;
208                         GO_STATE(EC2_TRACE) ;
209                         break ;
210                 }
211                 /*EC13*/
212                 else if (cmd == EC_DISCONNECT) {
213                         GO_STATE(EC3_LEAVE) ;
214                         break ;
215                 }
216                 break;
217         case ACTIONS(EC2_TRACE) :
218                 start_ecm_timer(smc,MIB2US(smc->mib.fddiSMTTrace_MaxExpiration),
219                         EC_TIMEOUT_TMAX) ;
220                 ACTIONS_DONE() ;
221                 break ;
222         case EC2_TRACE :
223                 /*EC22*/
224                 if (cmd == EC_TRACE_PROP) {
225                         prop_actions(smc) ;
226                         GO_STATE(EC2_TRACE) ;
227                         break ;
228                 }
229                 /*EC23a*/
230                 else if (cmd == EC_DISCONNECT) {
231                         smc->e.path_test = PT_EXITING ;
232                         GO_STATE(EC3_LEAVE) ;
233                         break ;
234                 }
235                 /*EC23b*/
236                 else if (smc->e.path_test == PT_PENDING) {
237                         GO_STATE(EC3_LEAVE) ;
238                         break ;
239                 }
240                 /*EC23c*/
241                 else if (cmd == EC_TIMEOUT_TMAX) {
242                         /* Trace_Max is expired */
243                         /* -> send AIX_EVENT */
244                         AIX_EVENT(smc, (u_long) FDDI_RING_STATUS,
245                                 (u_long) FDDI_SMT_ERROR, (u_long)
246                                 FDDI_TRACE_MAX, smt_get_error_word(smc));
247                         smc->e.path_test = PT_PENDING ;
248                         GO_STATE(EC3_LEAVE) ;
249                         break ;
250                 }
251                 break ;
252         case ACTIONS(EC3_LEAVE) :
253                 start_ecm_timer(smc,smc->s.ecm_td_min,EC_TIMEOUT_TD) ;
254                 for (p = 0 ; p < NUMPHYS ; p++)
255                         queue_event(smc,EVENT_PCMA+p,PC_STOP) ;
256                 ACTIONS_DONE() ;
257                 break ;
258         case EC3_LEAVE:
259                 /*EC30*/
260                 if (cmd == EC_TIMEOUT_TD && !smc->mib.fddiSMTBypassPresent &&
261                         (smc->e.path_test != PT_PENDING)) {
262                         GO_STATE(EC0_OUT) ;
263                         break ;
264                 }
265                 /*EC34*/
266                 else if (cmd == EC_TIMEOUT_TD &&
267                         (smc->e.path_test == PT_PENDING)) {
268                         GO_STATE(EC4_PATH_TEST) ;
269                         break ;
270                 }
271                 /*EC31*/
272                 else if (cmd == EC_CONNECT && smc->e.path_test == PT_PASSED) {
273                         GO_STATE(EC1_IN) ;
274                         break ;
275                 }
276                 /*EC33*/
277                 else if (cmd == EC_DISCONNECT &&
278                         smc->e.path_test == PT_PENDING) {
279                         smc->e.path_test = PT_EXITING ;
280                         /*
281                          * stay in state - state will be left via timeout
282                          */
283                 }
284                 /*EC37*/
285                 else if (cmd == EC_TIMEOUT_TD &&
286                         smc->mib.fddiSMTBypassPresent &&
287                         smc->e.path_test != PT_PENDING) {
288                         GO_STATE(EC7_DEINSERT) ;
289                         break ;
290                 }
291                 break ;
292         case ACTIONS(EC4_PATH_TEST) :
293                 stop_ecm_timer(smc) ;
294                 smc->e.path_test = PT_TESTING ;
295                 start_ecm_timer(smc,smc->s.ecm_test_done,EC_TEST_DONE) ;
296                 /* now perform path test ... just a simulation */
297                 ACTIONS_DONE() ;
298                 break ;
299         case EC4_PATH_TEST :
300                 /* path test done delay */
301                 if (cmd == EC_TEST_DONE)
302                         smc->e.path_test = PT_PASSED ;
303
304                 if (smc->e.path_test == PT_FAILED)
305                         RS_SET(smc,RS_PATHTEST) ;
306
307                 /*EC40a*/
308                 if (smc->e.path_test == PT_FAILED &&
309                         !smc->mib.fddiSMTBypassPresent) {
310                         GO_STATE(EC0_OUT) ;
311                         break ;
312                 }
313                 /*EC40b*/
314                 else if (cmd == EC_DISCONNECT &&
315                         !smc->mib.fddiSMTBypassPresent) {
316                         GO_STATE(EC0_OUT) ;
317                         break ;
318                 }
319                 /*EC41*/
320                 else if (smc->e.path_test == PT_PASSED) {
321                         GO_STATE(EC1_IN) ;
322                         break ;
323                 }
324                 /*EC47a*/
325                 else if (smc->e.path_test == PT_FAILED &&
326                         smc->mib.fddiSMTBypassPresent) {
327                         GO_STATE(EC7_DEINSERT) ;
328                         break ;
329                 }
330                 /*EC47b*/
331                 else if (cmd == EC_DISCONNECT &&
332                         smc->mib.fddiSMTBypassPresent) {
333                         GO_STATE(EC7_DEINSERT) ;
334                         break ;
335                 }
336                 break ;
337         case ACTIONS(EC5_INSERT) :
338                 sm_pm_bypass_req(smc,BP_INSERT);
339                 start_ecm_timer(smc,smc->s.ecm_in_max,EC_TIMEOUT_INMAX) ;
340                 ACTIONS_DONE() ;
341                 break ;
342         case EC5_INSERT :
343                 /*EC56*/
344                 if (cmd == EC_TIMEOUT_INMAX) {
345                         GO_STATE(EC6_CHECK) ;
346                         break ;
347                 }
348                 /*EC57*/
349                 else if (cmd == EC_DISCONNECT) {
350                         GO_STATE(EC7_DEINSERT) ;
351                         break ;
352                 }
353                 break ;
354         case ACTIONS(EC6_CHECK) :
355                 /*
356                  * in EC6_CHECK, we *POLL* the line state !
357                  * check whether both bypass switches have switched.
358                  */
359                 start_ecm_timer(smc,smc->s.ecm_check_poll,0) ;
360                 smc->e.ecm_line_state = TRUE ;  /* flag to pcm: report Q/HLS */
361                 (void) sm_pm_ls_latch(smc,PA,1) ; /* enable line state latch */
362                 (void) sm_pm_ls_latch(smc,PB,1) ; /* enable line state latch */
363                 ACTIONS_DONE() ;
364                 break ;
365         case EC6_CHECK :
366                 ls_a = sm_pm_get_ls(smc,PA) ;
367                 ls_b = sm_pm_get_ls(smc,PB) ;
368
369                 /*EC61*/
370                 if (((ls_a == PC_QLS) || (ls_a == PC_HLS)) &&
371                     ((ls_b == PC_QLS) || (ls_b == PC_HLS)) ) {
372                         smc->e.sb_flag = FALSE ;
373                         smc->e.ecm_line_state = FALSE ;
374                         GO_STATE(EC1_IN) ;
375                         break ;
376                 }
377                 /*EC66*/
378                 else if (!smc->e.sb_flag &&
379                          (((ls_a == PC_ILS) && (ls_b == PC_QLS)) ||
380                           ((ls_a == PC_QLS) && (ls_b == PC_ILS)))){
381                         smc->e.sb_flag = TRUE ;
382                         DB_ECMN(1,"ECM : EC6_CHECK - stuck bypass\n",0,0) ;
383                         AIX_EVENT(smc, (u_long) FDDI_RING_STATUS, (u_long)
384                                 FDDI_SMT_ERROR, (u_long) FDDI_BYPASS_STUCK,
385                                 smt_get_error_word(smc));
386                 }
387                 /*EC67*/
388                 else if (cmd == EC_DISCONNECT) {
389                         smc->e.ecm_line_state = FALSE ;
390                         GO_STATE(EC7_DEINSERT) ;
391                         break ;
392                 }
393                 else {
394                         /*
395                          * restart poll
396                          */
397                         start_ecm_timer(smc,smc->s.ecm_check_poll,0) ;
398                 }
399                 break ;
400         case ACTIONS(EC7_DEINSERT) :
401                 sm_pm_bypass_req(smc,BP_DEINSERT);
402                 start_ecm_timer(smc,smc->s.ecm_i_max,EC_TIMEOUT_IMAX) ;
403                 ACTIONS_DONE() ;
404                 break ;
405         case EC7_DEINSERT:
406                 /*EC70*/
407                 if (cmd == EC_TIMEOUT_IMAX) {
408                         GO_STATE(EC0_OUT) ;
409                         break ;
410                 }
411                 /*EC75*/
412                 else if (cmd == EC_CONNECT && smc->e.path_test == PT_PASSED) {
413                         GO_STATE(EC5_INSERT) ;
414                         break ;
415                 }
416                 break;
417         default:
418                 SMT_PANIC(smc,SMT_E0107, SMT_E0107_MSG) ;
419                 break;
420         }
421 }
422
423 #ifndef CONCENTRATOR
424 /*
425  * trace propagation actions for SAS & DAS
426  */
427 static void prop_actions(struct s_smc *smc)
428 {
429         int     port_in = 0 ;
430         int     port_out = 0 ;
431
432         RS_SET(smc,RS_EVENT) ;
433         switch (smc->s.sas) {
434         case SMT_SAS :
435                 port_in = port_out = pcm_get_s_port(smc) ;
436                 break ;
437         case SMT_DAS :
438                 port_in = cfm_get_mac_input(smc) ;      /* PA or PB */
439                 port_out = cfm_get_mac_output(smc) ;    /* PA or PB */
440                 break ;
441         case SMT_NAC :
442                 SMT_PANIC(smc,SMT_E0108, SMT_E0108_MSG) ;
443                 return ;
444         }
445
446         DB_ECM("ECM : prop_actions - trace_prop %d\n", smc->e.trace_prop,0) ;
447         DB_ECM("ECM : prop_actions - in %d out %d\n", port_in,port_out) ;
448
449         if (smc->e.trace_prop & ENTITY_BIT(ENTITY_MAC)) {
450                 /* trace initiatior */
451                 DB_ECM("ECM : initiate TRACE on PHY %c\n",'A'+port_in-PA,0) ;
452                 queue_event(smc,EVENT_PCM+port_in,PC_TRACE) ;
453         }
454         else if ((smc->e.trace_prop & ENTITY_BIT(ENTITY_PHY(PA))) &&
455                 port_out != PA) {
456                 /* trace propagate upstream */
457                 DB_ECM("ECM : propagate TRACE on PHY B\n",0,0) ;
458                 queue_event(smc,EVENT_PCMB,PC_TRACE) ;
459         }
460         else if ((smc->e.trace_prop & ENTITY_BIT(ENTITY_PHY(PB))) &&
461                 port_out != PB) {
462                 /* trace propagate upstream */
463                 DB_ECM("ECM : propagate TRACE on PHY A\n",0,0) ;
464                 queue_event(smc,EVENT_PCMA,PC_TRACE) ;
465         }
466         else {
467                 /* signal trace termination */
468                 DB_ECM("ECM : TRACE terminated\n",0,0) ;
469                 smc->e.path_test = PT_PENDING ;
470         }
471         smc->e.trace_prop = 0 ;
472 }
473 #else
474 /*
475  * trace propagation actions for Concentrator
476  */
477 static void prop_actions(struct s_smc *smc)
478 {
479         int     initiator ;
480         int     upstream ;
481         int     p ;
482
483         RS_SET(smc,RS_EVENT) ;
484         while (smc->e.trace_prop) {
485                 DB_ECM("ECM : prop_actions - trace_prop %d\n",
486                         smc->e.trace_prop,0) ;
487
488                 if (smc->e.trace_prop & ENTITY_BIT(ENTITY_MAC)) {
489                         initiator = ENTITY_MAC ;
490                         smc->e.trace_prop &= ~ENTITY_BIT(ENTITY_MAC) ;
491                         DB_ECM("ECM: MAC initiates trace\n",0,0) ;
492                 }
493                 else {
494                         for (p = NUMPHYS-1 ; p >= 0 ; p--) {
495                                 if (smc->e.trace_prop &
496                                         ENTITY_BIT(ENTITY_PHY(p)))
497                                         break ;
498                         }
499                         initiator = ENTITY_PHY(p) ;
500                         smc->e.trace_prop &= ~ENTITY_BIT(ENTITY_PHY(p)) ;
501                 }
502                 upstream = cem_get_upstream(smc,initiator) ;
503
504                 if (upstream == ENTITY_MAC) {
505                         /* signal trace termination */
506                         DB_ECM("ECM : TRACE terminated\n",0,0) ;
507                         smc->e.path_test = PT_PENDING ;
508                 }
509                 else {
510                         /* trace propagate upstream */
511                         DB_ECM("ECM : propagate TRACE on PHY %d\n",upstream,0) ;
512                         queue_event(smc,EVENT_PCM+upstream,PC_TRACE) ;
513                 }
514         }
515 }
516 #endif
517
518
519 /*
520  * SMT timer interface
521  *      start ECM timer
522  */
523 static void start_ecm_timer(struct s_smc *smc, u_long value, int event)
524 {
525         smt_timer_start(smc,&smc->e.ecm_timer,value,EV_TOKEN(EVENT_ECM,event));
526 }
527
528 /*
529  * SMT timer interface
530  *      stop ECM timer
531  */
532 static void stop_ecm_timer(struct s_smc *smc)
533 {
534         if (smc->e.ecm_timer.tm_active)
535                 smt_timer_stop(smc,&smc->e.ecm_timer) ;
536 }