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 timer
15*/
16
17#include "h/types.h"
18#include "h/fddi.h"
19#include "h/smc.h"
20
21#ifndef	lint
22static const char ID_sccs[] = "@(#)smttimer.c	2.4 97/08/04 (C) SK " ;
23#endif
24
25static void timer_done(struct s_smc *smc, int restart);
26
27void smt_timer_init(struct s_smc *smc)
28{
29	smc->t.st_queue = NULL;
30	smc->t.st_fast.tm_active = FALSE ;
31	smc->t.st_fast.tm_next = NULL;
32	hwt_init(smc) ;
33}
34
35void smt_timer_stop(struct s_smc *smc, struct smt_timer *timer)
36{
37	struct smt_timer	**prev ;
38	struct smt_timer	*tm ;
39
40	/*
41	 * remove timer from queue
42	 */
43	timer->tm_active = FALSE ;
44	if (smc->t.st_queue == timer && !timer->tm_next) {
45		hwt_stop(smc) ;
46	}
47	for (prev = &smc->t.st_queue ; (tm = *prev) ; prev = &tm->tm_next ) {
48		if (tm == timer) {
49			*prev = tm->tm_next ;
50			if (tm->tm_next) {
51				tm->tm_next->tm_delta += tm->tm_delta ;
52			}
53			return ;
54		}
55	}
56}
57
58void smt_timer_start(struct s_smc *smc, struct smt_timer *timer, u_long time,
59		     u_long token)
60{
61	struct smt_timer	**prev ;
62	struct smt_timer	*tm ;
63	u_long			delta = 0 ;
64
65	time /= 16 ;		/* input is uS, clock ticks are 16uS */
66	if (!time)
67		time = 1 ;
68	smt_timer_stop(smc,timer) ;
69	timer->tm_smc = smc ;
70	timer->tm_token = token ;
71	timer->tm_active = TRUE ;
72	if (!smc->t.st_queue) {
73		smc->t.st_queue = timer ;
74		timer->tm_next = NULL;
75		timer->tm_delta = time ;
76		hwt_start(smc,time) ;
77		return ;
78	}
79	/*
80	 * timer correction
81	 */
82	timer_done(smc,0) ;
83
84	/*
85	 * find position in queue
86	 */
87	delta = 0 ;
88	for (prev = &smc->t.st_queue ; (tm = *prev) ; prev = &tm->tm_next ) {
89		if (delta + tm->tm_delta > time) {
90			break ;
91		}
92		delta += tm->tm_delta ;
93	}
94	/* insert in queue */
95	*prev = timer ;
96	timer->tm_next = tm ;
97	timer->tm_delta = time - delta ;
98	if (tm)
99		tm->tm_delta -= timer->tm_delta ;
100	/*
101	 * start new with first
102	 */
103	hwt_start(smc,smc->t.st_queue->tm_delta) ;
104}
105
106void smt_force_irq(struct s_smc *smc)
107{
108	smt_timer_start(smc,&smc->t.st_fast,32L, EV_TOKEN(EVENT_SMT,SM_FAST));
109}
110
111void smt_timer_done(struct s_smc *smc)
112{
113	timer_done(smc,1) ;
114}
115
116static void timer_done(struct s_smc *smc, int restart)
117{
118	u_long			delta ;
119	struct smt_timer	*tm ;
120	struct smt_timer	*next ;
121	struct smt_timer	**last ;
122	int			done = 0 ;
123
124	delta = hwt_read(smc) ;
125	last = &smc->t.st_queue ;
126	tm = smc->t.st_queue ;
127	while (tm && !done) {
128		if (delta >= tm->tm_delta) {
129			tm->tm_active = FALSE ;
130			delta -= tm->tm_delta ;
131			last = &tm->tm_next ;
132			tm = tm->tm_next ;
133		}
134		else {
135			tm->tm_delta -= delta ;
136			delta = 0 ;
137			done = 1 ;
138		}
139	}
140	*last = NULL;
141	next = smc->t.st_queue ;
142	smc->t.st_queue = tm ;
143
144	for ( tm = next ; tm ; tm = next) {
145		next = tm->tm_next ;
146		timer_event(smc,tm->tm_token) ;
147	}
148
149	if (restart && smc->t.st_queue)
150		hwt_start(smc,smc->t.st_queue->tm_delta) ;
151}
152
153