summaryrefslogtreecommitdiff
path: root/os/pxa/clock.c
diff options
context:
space:
mode:
Diffstat (limited to 'os/pxa/clock.c')
-rw-r--r--os/pxa/clock.c318
1 files changed, 318 insertions, 0 deletions
diff --git a/os/pxa/clock.c b/os/pxa/clock.c
new file mode 100644
index 00000000..2fd6eba6
--- /dev/null
+++ b/os/pxa/clock.c
@@ -0,0 +1,318 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+#include "ureg.h"
+
+static ulong timer_incr[4] = { 0, 0, 0, -1 };
+
+typedef struct Clock0link Clock0link;
+typedef struct Clock0link {
+ void (*clock)(void);
+ Clock0link* link;
+} Clock0link;
+
+static Clock0link *clock0link;
+static Lock clock0lock;
+static void (*prof_fcn)(Ureg *, int);
+
+Timer*
+addclock0link(void (*clock)(void), int)
+{
+ Clock0link *lp;
+
+ if((lp = malloc(sizeof(Clock0link))) == 0){
+ print("addclock0link: too many links\n");
+ return nil;
+ }
+ ilock(&clock0lock);
+ lp->clock = clock;
+ lp->link = clock0link;
+ clock0link = lp;
+ iunlock(&clock0lock);
+ return nil;
+}
+
+static void
+profintr(Ureg *ur, void*)
+{
+ OstmrReg *ost = OSTMRREG;
+ int t;
+
+ if((ost->osmr[3] - ost->oscr) < 2*CLOCKFREQ) {
+ /* less than 2 seconds before reset, say something */
+ setpanic();
+ clockpoll();
+ dumpregs(ur);
+ panic("Watchdog timer will expire");
+ }
+
+ /* advance the profile clock tick */
+ ost->osmr[2] += timer_incr[2];
+ ost->ossr = (1 << 2); /* Clear the SR */
+ t = 1;
+ while((ost->osmr[2] - ost->oscr) > 0x80000000) {
+ ost->osmr[2] += timer_incr[2];
+ t++;
+ }
+ if(prof_fcn)
+ prof_fcn(ur, t);
+}
+
+static void
+clockintr(Ureg*, void*)
+{
+ Clock0link *lp;
+ int losttick = 0;
+ OstmrReg *ost = OSTMRREG;
+
+{static int tag, led; if(++tag >= HZ){ledset(led ^= 1);tag=0;}}
+ m->ticks++;
+// ost->osmr[3] = ost->oscr + timer_incr[3]; /* restart the watchdog */
+ ost->osmr[0] += timer_incr[0]; /* advance the clock tick */
+ ost->ossr = (1<<0); /* Clear the SR */
+
+ while((ost->osmr[0] - ost->oscr) >= 0x80000000) {
+ ost->osmr[0] += timer_incr[0];
+ losttick++;
+ m->ticks++;
+ }
+
+ checkalarms();
+
+ if(canlock(&clock0lock)){
+ for(lp = clock0link; lp; lp = lp->link)
+ if(lp->clock)
+ lp->clock();
+ unlock(&clock0lock);
+ }
+
+ /* round robin time slice is done by trap.c and proc.c */
+}
+
+void
+timerenable( int timer, int Hz, void (*f)(Ureg *, void*), void* a)
+{
+ OstmrReg *ost = OSTMRREG;
+ char name[KNAMELEN];
+
+ if(timer < 0 || timer > 3)
+ return;
+ timer_incr[timer] = CLOCKFREQ/Hz; /* set up freq */
+ ost->osmr[timer] = ost->oscr+timer_incr[timer];
+ snprint(name, sizeof(name), "timer%d", timer);
+ intrenable(IRQ, IRQtimer0+timer, f, a, name);
+ ost->ossr = (1 << timer); /* clear any pending interrupt */
+ ost->oier |= (1 << timer); /* enable interrupt */
+}
+
+void
+timerdisable( int timer )
+{
+ OstmrReg *ost = OSTMRREG;
+
+ if(timer < 0 || timer > 3)
+ return;
+ ost->osmr[timer] = 0; /* clear freq */
+ ost->oier &= ~(1 << timer); /* disable interrupt */
+}
+
+void
+installprof(void (*pf)(Ureg *, int))
+{
+ int s;
+
+ s = splfhi();
+ prof_fcn = pf;
+ timerenable( 2, HZ+1, profintr, 0);
+ timer_incr[2] = timer_incr[0]+63; /* fine tuning */
+ splx(s);
+}
+
+void
+clockinit(void)
+{
+ OstmrReg *ost = OSTMRREG;
+ m->ticks = 0;
+ /* Set up timer registers */
+ ost->ossr = 0xf; /* clear all four OSSR trigger bits */
+ ost->oier = 0;
+ ost->osmr[0] = 0;
+ ost->osmr[1] = 0;
+ ost->osmr[2] = 0;
+ // ost->osmr[3] = 0;
+ timerenable( 0, HZ, clockintr, 0);
+// timer_incr[3] = CLOCKFREQ*10; /* 10 second watchdog */
+// timer_setwatchdog(timer_incr[3]);
+// timerenable( 2, 1, profintr, 0); /* watch the watchdog */
+}
+
+void
+clockpoll(void)
+{
+ OstmrReg *ost = OSTMRREG;
+// ost->osmr[3] = ost->oscr + timer_incr[3]; /* restart the watchdog */
+}
+
+void
+clockcheck(void)
+{
+ OstmrReg *ost = OSTMRREG;
+
+ if((ost->osmr[3] - ost->oscr) < CLOCKFREQ) {
+ setpanic();
+ clockpoll();
+ dumpstack();
+ panic("Watchdog timer will expire");
+ }
+}
+
+// macros for fixed-point math
+
+ulong _mularsv(ulong m0, ulong m1, ulong a, ulong s);
+
+/* truncated: */
+#define FXDPTDIV(a,b,n) ((ulong)(((uvlong)(a) << (n)) / (b)))
+#define MAXMUL(a,n) ((ulong)((((uvlong)1<<(n))-1)/(a)))
+#define MULDIV(x,a,b,n) (((x)*FXDPTDIV(a,b,n)) >> (n))
+#define MULDIV64(x,a,b,n) ((ulong)_mularsv(x, FXDPTDIV(a,b,n), 0, (n)))
+
+/* rounded: */
+#define FXDPTDIVR(a,b,n) ((ulong)((((uvlong)(a) << (n))+((b)/2)) / (b)))
+#define MAXMULR(a,n) ((ulong)((((uvlong)1<<(n))-1)/(a)))
+#define MULDIVR(x,a,b,n) (((x)*FXDPTDIVR(a,b,n)+(1<<((n)-1))) >> (n))
+#define MULDIVR64(x,a,b,n) ((ulong)_mularsv(x, FXDPTDIVR(a,b,n), 1<<((n)-1), (n)))
+
+
+// these routines are all limited to a maximum of 1165 seconds,
+// due to the wrap-around of the OSTIMER
+
+ulong
+timer_start(void)
+{
+ return OSTMRREG->oscr;
+}
+
+ulong
+timer_ticks(ulong t0)
+{
+ return OSTMRREG->oscr - t0;
+}
+
+int
+timer_devwait(ulong *adr, ulong mask, ulong val, int ost)
+{
+ int i;
+ ulong t0 = timer_start();
+ while((*adr & mask) != val)
+ if(timer_ticks(t0) > ost)
+ return ((*adr & mask) == val) ? 0 : -1;
+ else
+ for(i = 0; i < 10; i++); /* don't pound OSCR too hard! (why not?) */
+ return 0;
+}
+
+void
+timer_setwatchdog(int t)
+{
+ OstmrReg *ost = OSTMRREG;
+ ost->osmr[3] = ost->oscr + t;
+ if(t) {
+ ost->ossr = (1<<3);
+ ost->oier |= (1<<3);
+ ost->ower = 1;
+ } else
+ ost->oier &= ~(1<<3);
+}
+
+void
+timer_delay(int t)
+{
+ ulong t0 = timer_start();
+ while(timer_ticks(t0) < t)
+ ;
+}
+
+
+ulong
+us2tmr(int us)
+{
+ return MULDIV64(us, CLOCKFREQ, 1000000, 24);
+}
+
+int
+tmr2us(ulong t)
+{
+ return MULDIV64(t, 1000000, CLOCKFREQ, 24);
+}
+
+void
+microdelay(int us)
+{
+ ulong t0 = timer_start();
+ ulong t = us2tmr(us);
+ while(timer_ticks(t0) <= t)
+ ;
+}
+
+ulong
+ms2tmr(int ms)
+{
+ return MULDIV64(ms, CLOCKFREQ, 1000, 20);
+}
+
+int
+tmr2ms(ulong t)
+{
+ return MULDIV64(t, 1000, CLOCKFREQ, 32);
+}
+
+void
+delay(int ms)
+{
+ ulong t0 = timer_start();
+ ulong t = ms2tmr(ms);
+ while(timer_ticks(t0) <= t)
+ clockpoll();
+}
+
+/*
+ * for devbench.c
+ */
+vlong
+archrdtsc(void)
+{
+ return OSTMRREG->oscr;
+}
+
+ulong
+archrdtsc32(void)
+{
+ return OSTMRREG->oscr;
+}
+
+/*
+ * for devkprof.c
+ */
+long
+archkprofmicrosecondspertick(void)
+{
+ return MS2HZ*1000;
+}
+
+void
+archkprofenable(int)
+{
+ /* TO DO */
+}
+
+uvlong
+fastticks(uvlong *hz)
+{
+ if(hz)
+ *hz = HZ;
+ return m->ticks;
+}