File: [local] / sys / arch / mvmeppc / dev / clock.c (download)
Revision 1.1.1.1 (vendor branch), Tue Mar 4 16:07:44 2008 UTC (16 years, 6 months ago) by nbrk
Branch: OPENBSD_4_2_BASE, MAIN
CVS Tags: jornada-partial-support-wip, HEAD Changes since 1.1: +0 -0 lines
Import of OpenBSD 4.2 release kernel tree with initial code to support
Jornada 720/728, StrongARM 1110-based handheld PC.
At this point kernel roots on NFS and boots into vfs_mountroot() and traps.
What is supported:
- glass console, Jornada framebuffer (jfb) works in 16bpp direct color mode
(needs some palette tweaks for non black/white/blue colors, i think)
- saic, SA11x0 interrupt controller (needs cleanup)
- sacom, SA11x0 UART (supported only as boot console for now)
- SA11x0 GPIO controller fully supported (but can't handle multiple interrupt
handlers on one gpio pin)
- sassp, SSP port on SA11x0 that attaches spibus
- Jornada microcontroller (jmcu) to control kbd, battery, etc throught
the SPI bus (wskbd attaches on jmcu, but not tested)
- tod functions seem work
- initial code for SA-1111 (chip companion) : this is TODO
Next important steps, i think:
- gpio and intc on sa1111
- pcmcia support for sa11x0 (and sa1111 help logic)
- REAL root on nfs when we have PCMCIA support (we may use any of supported pccard NICs)
- root on wd0! (using already supported PCMCIA-ATA)
|
/* $OpenBSD: clock.c,v 1.10 2004/12/24 22:50:30 miod Exp $ */
/* $NetBSD: clock.c,v 1.1 1996/09/30 16:34:40 ws Exp $ */
/*
* Copyright (C) 1995, 1996 Wolfgang Solfrank.
* Copyright (C) 1995, 1996 TooLs GmbH.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by TooLs GmbH.
* 4. The name of TooLs GmbH may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/systm.h>
#include <machine/pio.h>
#include <machine/intr.h>
#include <machine/powerpc.h>
#include "bugtty.h"
void resettodr(void);
void decr_intr(struct clockframe *);
void calc_delayconst(void);
/*
* Initially we assume a processor with a bus frequency of 12.5 MHz.
*/
static u_long ticks_per_sec = 3125000;
static u_long ns_per_tick = 320;
static long ticks_per_intr;
static volatile u_long lasttb;
/*
* BCD to decimal and decimal to BCD.
*/
#define FROMBCD(x) (((x) >> 4) * 10 + ((x) & 0xf))
#define TOBCD(x) (((x) / 10 * 16) + ((x) % 10))
#define SECDAY (24 * 60 * 60)
#define SECYR (SECDAY * 365)
#define LEAPYEAR(y) (((y) & 3) == 0)
#define YEAR0 1900
tps_t *tps;
clock_read_t *clock_read;
clock_write_t *clock_write;
time_read_t *time_read;
time_write_t *time_write;
static u_int32_t chiptotime(int, int, int, int, int, int);
/* event tracking variables, when the next event of each time should occur */
u_int64_t nexttimerevent, prevtb, nextstatevent;
/* vars for stats */
int statint;
u_int32_t statvar;
u_int32_t statmin;
struct chiptime {
int sec;
int min;
int hour;
int wday;
int day;
int mon;
int year;
};
static void timetochip(struct chiptime *c);
/*
* For now we let the machine run with boot time, not changing the clock
* at inittodr at all.
*
* We might continue to do this due to setting up the real wall clock with
* a user level utility in the future.
*/
/* ARGSUSED */
void
inittodr(time_t base)
{
int sec, min, hour, day, mon, year;
int badbase = 0, waszero = base == 0;
if (base < 5 * SECYR) {
/*
* If base is 0, assume filesystem time is just unknown
* instead of preposterous. Don't bark.
*/
if (base != 0)
printf("WARNING: preposterous time in file system\n");
/* not going to use it anyway, if the chip is readable */
base = 21*SECYR + 186*SECDAY + SECDAY/2;
badbase = 1;
}
if (clock_read != NULL ) {
(*clock_read)( &sec, &min, &hour, &day, &mon, &year);
time.tv_sec = chiptotime(sec, min, hour, day, mon, year);
} else if (time_read != NULL) {
u_int32_t cursec;
(*time_read)(&cursec);
time.tv_sec = cursec;
} else {
/* force failure */
time.tv_sec = 0;
}
if (time.tv_sec == 0) {
printf("WARNING: unable to get date/time");
/*
* Believe the time in the file system for lack of
* anything better, resetting the clock.
*/
time.tv_sec = base;
if (!badbase)
resettodr();
} else {
int deltat;
time.tv_sec += tz.tz_minuteswest * 60;
if (tz.tz_dsttime)
time.tv_sec -= 3600;
deltat = time.tv_sec - base;
if (deltat < 0)
deltat = -deltat;
if (waszero || deltat < 2 * SECDAY)
return;
printf("WARNING: clock %s %d days",
time.tv_sec < base ? "lost" : "gained", deltat / SECDAY);
if (time.tv_sec < base && deltat > 1000 * SECDAY) {
printf(", using FS time");
time.tv_sec = base;
}
}
printf(" -- CHECK AND RESET THE DATE!\n");
}
/*
* This code is defunct after 2068.
* Will Unix still be here then??
*/
const short dayyr[12] =
{ 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334};
static u_int32_t
chiptotime(int sec, int min, int hour, int day, int mon, int year)
{
int days, yr;
sec = FROMBCD(sec);
min = FROMBCD(min);
hour = FROMBCD(hour);
day = FROMBCD(day);
mon = FROMBCD(mon);
year = FROMBCD(year) + YEAR0;
/* simple sanity checks */
if (year < 1970 || mon < 1 || mon > 12 || day < 1 || day > 31)
return (0);
days = 0;
for (yr = 1970; yr < year; yr++)
days += LEAPYEAR(yr) ? 366 : 365;
days += dayyr[mon - 1] + day - 1;
if (LEAPYEAR(yr) && mon > 2)
days++;
/* now have days since Jan 1, 1970; the rest is easy... */
return (days * SECDAY + hour * 3600 + min * 60 + sec);
}
void
timetochip(struct chiptime *c)
{
int t, t2, t3, now = time.tv_sec;
/* January 1 1970 was a Thursday (4 in unix wdays) */
/* compute the days since the epoch */
t2 = now / SECDAY;
t3 = (t2 + 4) % 7; /* day of week */
c->wday = TOBCD(t3 + 1);
/* compute the year */
t = 69;
while (t2 >= 0) { /* whittle off years */
t3 = t2;
t++;
t2 -= LEAPYEAR(t) ? 366 : 365;
}
c->year = t;
/* t3 = month + day; separate */
t = LEAPYEAR(t);
for (t2 = 1; t2 < 12; t2++)
if (t3 < (dayyr[t2] + ((t && (t2 > 1)) ? 1:0)))
break;
/* t2 is month */
c->mon = t2;
c->day = t3 - dayyr[t2 - 1] + 1;
if (t && t2 > 2)
c->day--;
/* the rest is easy */
t = now % SECDAY;
c->hour = t / 3600;
t %= 3600;
c->min = t / 60;
c->sec = t % 60;
c->sec = TOBCD(c->sec);
c->min = TOBCD(c->min);
c->hour = TOBCD(c->hour);
c->day = TOBCD(c->day);
c->mon = TOBCD(c->mon);
c->year = TOBCD((c->year - YEAR0) % 100);
}
/*
* Similar to the above
*/
void
resettodr()
{
struct timeval curtime = time;
if (clock_write != NULL) {
struct chiptime c;
timetochip(&c);
(*clock_write)(c.sec, c.min, c.hour, c.day, c.mon, c.year);
} else if (time_write != NULL) {
curtime.tv_sec -= tz.tz_minuteswest * 60;
if (tz.tz_dsttime) {
curtime.tv_sec += 3600;
}
(*time_write)(curtime.tv_sec);
}
}
volatile int statspending;
void
decr_intr(struct clockframe *frame)
{
u_int64_t tb;
u_int64_t nextevent;
int nstats;
int s;
/*
* Check whether we are initialized.
*/
if (!ticks_per_intr)
return;
/*
* Based on the actual time delay since the last decrementer reload,
* we arrange for earlier interrupt next time.
*/
tb = ppc_mftb();
while (nexttimerevent <= tb)
nexttimerevent += ticks_per_intr;
prevtb = nexttimerevent - ticks_per_intr;
for (nstats = 0; nextstatevent <= tb; nstats++) {
int r;
do {
r = random() & (statvar - 1);
} while (r == 0); /* random == 0 not allowed */
nextstatevent += statmin + r;
}
if (nexttimerevent < nextstatevent)
nextevent = nexttimerevent;
else
nextevent = nextstatevent;
/*
* Need to work about the near constant skew this introduces???
* reloading tb here could cause a missed tick.
*/
ppc_mtdec(nextevent - tb);
if (cpl & SPL_CLOCK) {
statspending += nstats;
} else {
nstats += statspending;
statspending = 0;
s = splclock();
/*
* Reenable interrupts
*/
ppc_intr_enable(1);
/*
* Do standard timer interrupt stuff.
* Do softclock stuff only on the last iteration.
*/
frame->pri = s | SINT_CLOCK;
while (lasttb < prevtb - ticks_per_intr) {
/* sync lasttb with hardclock */
lasttb += ticks_per_intr;
hardclock(frame);
}
frame->pri = s;
while (lasttb < prevtb) {
/* sync lasttb with hardclock */
lasttb += ticks_per_intr;
hardclock(frame);
#if NBUGTTY > 0
{
extern void bugtty_chkinput(void);
bugtty_chkinput();
}
#endif
}
while (nstats-- > 0)
statclock(frame);
splx(s);
ppc_intr_disable();
/*
* If a tick has occurred while dealing with these,
* don't service it now, delay until the next tick.
*/
}
}
void
cpu_initclocks()
{
int intrstate;
int r;
int minint;
u_int64_t nextevent;
intrstate = ppc_intr_disable();
stathz = 100;
profhz = 1000; /* must be a multiple of stathz */
/* init secondary clock to stathz */
statint = ticks_per_sec / stathz;
statvar = 0x40000000; /* really big power of two */
/* find largest 2^n which is nearly smaller than statint/2 */
minint = statint / 2 + 100;
while (statvar > minint)
statvar >>= 1;
statmin = statint - (statvar >> 1);
lasttb = ppc_mftb();
nexttimerevent = lasttb + ticks_per_intr;
do {
r = random() & (statvar - 1);
} while (r == 0); /* random == 0 not allowed */
nextstatevent = lasttb + statmin + r;
if (nexttimerevent < nextstatevent)
nextevent = nexttimerevent;
else
nextevent = nextstatevent;
ppc_mtdec(nextevent - lasttb);
ppc_intr_enable(intrstate);
}
void
calc_delayconst(void)
{
int s;
ticks_per_sec = (*tps)();
s = ppc_intr_disable();
ns_per_tick = 1000000000 / ticks_per_sec;
ticks_per_intr = ticks_per_sec / hz;
ppc_intr_enable(s);
}
/*
* Fill in *tvp with current time with microsecond resolution.
*/
void
microtime(struct timeval *tvp)
{
u_int64_t tb;
u_int32_t ticks;
int s;
s = ppc_intr_disable();
tb = ppc_mftb();
ticks = ((tb - lasttb) * ns_per_tick) / 1000;
*tvp = time;
ppc_intr_enable(s);
tvp->tv_usec += ticks;
while (tvp->tv_usec >= 1000000) {
tvp->tv_usec -= 1000000;
tvp->tv_sec++;
}
}
/*
* Wait for about n microseconds (us) (at least!).
*/
void
delay(unsigned n)
{
u_int64_t tb;
u_int32_t tbh, tbl, scratch;
tb = ppc_mftb();
tb += (n * 1000 + ns_per_tick - 1) / ns_per_tick;
tbh = tb >> 32;
tbl = (u_int32_t)tb;
asm ("1: mftbu %0; cmplw %0,%1; blt 1b; bgt 2f;"
" mftb %0; cmplw %0,%2; blt 1b; 2:"
:: "r"(scratch), "r"(tbh), "r"(tbl));
}
void
setstatclockrate(int newhz)
{
int minint;
int intrstate;
intrstate = ppc_intr_disable();
statint = ticks_per_sec / newhz;
statvar = 0x40000000; /* really big power of two */
/* find largest 2^n which is nearly smaller than statint/2 */
minint = statint / 2 + 100;
while (statvar > minint)
statvar >>= 1;
statmin = statint - (statvar >> 1);
ppc_intr_enable(intrstate);
/*
* XXX this allows the next stat timer to occur then it switches
* to the new frequency. Rather than switching instantly.
*/
}