xref: /inferno-os/os/mpc/cpmtimer.c (revision b43c1ca5eb5fc65b93ae935a568432712797b049)
1 #include "u.h"
2 #include "lib.h"
3 #include "mem.h"
4 #include "dat.h"
5 #include "fns.h"
6 #include "io.h"
7 
8 enum {
9 	Ntimer = 4	/* maximum allowed by hardware */
10 };
11 
12 static struct {
13 	Lock;
14 	int	init;
15 	int	ntimer;	/* actual timers on this chip revision */
16 	GTimer	t[Ntimer];
17 } cpmtimers;
18 
19 static	uchar	timerirq[] = {0x19, 0x12, 0x0C, 0x07};
20 
21 static	void	gtimerinit(int, ushort*, ushort*);
22 
23 static void
24 gtimerreset(void)
25 {
26 	IMM *io;
27 	int i;
28 
29 	ilock(&cpmtimers);
30 	if(!cpmtimers.init){
31 		if(m->cputype == 0x50 && (getimmr() & 0xFFFF) <= 0x2001)
32 			cpmtimers.ntimer = 2;
33 		else
34 			cpmtimers.ntimer = Ntimer;
35 		io = m->iomem;
36 		io->tgcr = 0x2222;	/* reset timers, low-power stop */
37 		for(i=0; i<cpmtimers.ntimer; i++)
38 			gtimerinit(i, &io->tmr1+i, &io->ter1+i);
39 		cpmtimers.init = 1;
40 	}
41 	iunlock(&cpmtimers);
42 }
43 
44 static void
45 gtimerintr(Ureg *ur, void *arg)
46 {
47 	GTimer *t;
48 
49 	t = arg;
50 	t->event = *t->ter;
51 	*t->ter = t->event;
52 	if(t->inuse && t->interrupt != nil)
53 		t->interrupt(ur, t->arg, t);
54 }
55 
56 static void
57 gtimerinit(int i, ushort *tmr, ushort *ter)
58 {
59 	GTimer *t;
60 	char name[KNAMELEN];
61 
62 	snprint(name, sizeof(name), "timer.%d", i);
63 	t = &cpmtimers.t[i];
64 	t->x = i*4;	/* field in tgcr */
65 	t->inuse = 0;
66 	t->interrupt = nil;
67 	t->tmr = tmr;
68 	t->trr = tmr+2;
69 	t->tcr = tmr+4;
70 	t->tcn = tmr+6;
71 	t->ter = ter;
72 	intrenable(VectorCPIC+timerirq[i], gtimerintr, t, BUSUNKNOWN, name);
73 }
74 
75 GTimer*
76 gtimer(ushort mode, ushort ref, void (*intr)(Ureg*,void*,GTimer*), void *arg)
77 {
78 	GTimer *t;
79 	int i;
80 
81 	t = cpmtimers.t;
82 	if(!cpmtimers.init)
83 		gtimerreset();
84 	ilock(&cpmtimers);
85 	for(i=0; ; i++){
86 		if(i >= cpmtimers.ntimer){
87 			iunlock(&cpmtimers);
88 			return nil;
89 		}
90 		if(t->inuse == 0)
91 			break;
92 		t++;
93 	}
94 	t->inuse = 1;
95 	t->interrupt = intr;
96 	t->arg = arg;
97 	m->iomem->tgcr &= ~(0xF<<t->x);	/* reset */
98 	*t->tmr = mode;
99 	*t->tcn = 0;
100 	*t->trr = ref;
101 	*t->ter = 0xFFFF;
102 	iunlock(&cpmtimers);
103 	return t;
104 }
105 
106 void
107 gtimerset(GTimer *t, ushort mode, int usec)
108 {
109 	ulong ref, ps;
110 	int clk;
111 
112 	if(usec <= 0)
113 		return;
114 	ref = usec*m->speed;
115 	clk = mode & (3<<1);
116 	if(ref >= 0x1000000 && clk == TimerSclk){
117 		mode = (mode & ~clk) | TimerSclk16;
118 		ref >>= 4;
119 	} else if(clk == TimerSclk16)
120 		ref >>= 4;
121 	ps = (ref+(1<<16))/(1<<16);	/* round up */
122 	ref /= ps;
123 	*t->tmr = ((ps-1)<<8) | (mode&0xFF);
124 	*t->trr = ref;
125 }
126 
127 void
128 gtimerstart(GTimer *t)
129 {
130 	if(t){
131 		ilock(&cpmtimers);
132 		m->iomem->tgcr = (m->iomem->tgcr & ~(0xF<<t->x)) | (1<<t->x);	/* enable */
133 		iunlock(&cpmtimers);
134 	}
135 }
136 
137 void
138 gtimerstop(GTimer *t)
139 {
140 	if(t){
141 		ilock(&cpmtimers);
142 		m->iomem->tgcr |= 2<<t->x;	/* stop */
143 		iunlock(&cpmtimers);
144 	}
145 }
146 
147 void
148 gtimerfree(GTimer *t)
149 {
150 	if(t){
151 		ilock(&cpmtimers);
152 		t->inuse = 0;
153 		*t->tmr = 0;	/* disable interrupts */
154 		*t->ter = 0xFFFF;
155 		m->iomem->tgcr = (m->iomem->tgcr & ~(0xF<<t->x)) | (2<<t->x);	/* reset and stop */
156 		iunlock(&cpmtimers);
157 	}
158 }
159 
160 #ifdef GTIMETEST
161 static void
162 gtintr(Ureg*, void*, GTimer*)
163 {
164 	m->bcsr[4] ^= DisableVideoLamp;	/* toggle an LED */
165 }
166 
167 void
168 gtimetest(void)
169 {
170 	GTimer *g;
171 
172 	g = gtimer(0, 0, gtintr, nil);
173 	gtimerset(g, TimerORI|TimerRestart|TimerSclk, 64000);
174 	gtimerstart(g);
175 	delay(1);
176 print("started timer: #%4.4ux #%4.4ux %8.8lux #%4.4ux #%4.4ux\n", *g->tmr, *g->trr, m->iomem->tgcr, *g->tcn, *g->ter);
177 print("ter=#%8.8lux tmr=#%8.8lux trr=#%8.8lux\n", g->ter, g->tmr, g->trr);
178 }
179 #endif
180