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