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