* Copyright (c) 1988 University of Utah.
* Copyright (c) 1982, 1990 The Regents of the University of California.
* This code is derived from software contributed to Berkeley by
* the Systems Programming Group of the University of Utah Computer
* %sccs.include.redist.c%
* from: Utah $Hdr: clock.c 1.18 91/01/21$
* @(#)clock.c 7.14 (Berkeley) %G%
#include "hp/dev/hilreg.h"
#include "../include/psl.h"
#include "../include/cpu.h"
static int clkint
; /* clock interval, as loaded */
* Statistics clock interval and variance, in usec. Variance must be a
* power of two. Since this gives us an even number, not an odd number,
* we discard one case and compensate. That is, a variance of 1024 would
* give us offsets in [0..1023]. Instead, we take offsets in [1..1023].
* This is symmetric about the point 512, or statvar/2, and thus averages
* to that value (assuming uniform random numbers).
static int statvar
= 1024 / 4; /* {stat,prof}clock variance */
static int statmin
; /* statclock interval - variance/2 */
static int profmin
; /* profclock interval - variance/2 */
static int timer3min
; /* current, from above choices */
static int month_days
[12] = {
31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
struct bbc_tm
*gmt_to_bbc();
u_char bbc_registers
[13];
u_char
write_bbc_reg(), read_bbc_reg();
struct hil_dev
*bbcaddr
= NULL
;
* Machine-dependent clock routines.
* A note on the real-time clock:
* We actually load the clock with interval-1 instead of interval.
* This is because the counter decrements to zero after N+1 enabled clock
* periods where N is the value loaded into the counter.
* The frequencies of the HP300 clocks must be a multiple of four
* microseconds (since the clock counts in 4 us units).
#define COUNTS_PER_SEC (1000000 / CLK_RESOLUTION)
* Set up the real-time and statistics clocks. Leave stathz 0 only if
* no alternative timer is available.
register volatile struct clkreg
*clk
;
register int intvl
, statint
, profint
, minint
;
clkstd
[0] = IIOV(0x5F8000); /* XXX grot */
clk
= (volatile struct clkreg
*)clkstd
[0];
if (COUNTS_PER_SEC
% hz
) {
printf("cannot get %d Hz clock; using 100 Hz\n", hz
);
* Clock has several counters, so we can always use separate
if (stathz
== 0) /* XXX should be set in param.c */
else if (COUNTS_PER_SEC
% stathz
) {
printf("cannot get %d Hz statclock; using 100 Hz\n", stathz
);
if (profhz
== 0) /* XXX should be set in param.c */
else if (profhz
< stathz
|| COUNTS_PER_SEC
% profhz
) {
printf("cannot get %d Hz profclock; using %d Hz\n",
intvl
= COUNTS_PER_SEC
/ hz
;
statint
= COUNTS_PER_SEC
/ stathz
;
profint
= COUNTS_PER_SEC
/ profhz
;
minint
= statint
/ 2 + 100;
tick
= intvl
* CLK_RESOLUTION
;
/* adjust interval counts, per note above */
/* calculate base reload values */
statmin
= statint
- (statvar
>> 1);
profmin
= profint
- (statvar
>> 1);
/* finally, load hardware */
clk
->clk_cr1
= CLK_RESET
;
clk
->clk_msb1
= intvl
>> 8;
clk
->clk_msb3
= statint
>> 8;
clk
->clk_cr1
= CLK_IENAB
;
clk
->clk_cr3
= CLK_IENAB
;
* We assume newhz is either stathz or profhz, and that neither will
* change after being set up above. Could recalculate intervals here
* but that would be a drag.
* Statistics/profiling clock interrupt. Clear the interrupt and
* compute a new interval.
* DO THIS INLINE IN locore.s?
register volatile struct clkreg
*clk
;
register int newint
, r
, var
;
clk
= (volatile struct clkreg
*)clkstd
[0];
discard
= clk
->clk_msb3
; /* clear interrupt */
r
= random() & (var
- 1);
clk
->clk_msb3
= newint
>> 8;
* Return the best possible estimate of the current time.
register struct timeval
*tvp
;
register volatile struct clkreg
*clk
;
register int s
, u
, h
, l
, sr
, l2
, h2
, u2
, s2
;
* Read registers from slowest-changing to fastest-changing,
* then re-read out to slowest. If the values read before the
* innermost match those read after, the innermost value is
* consistent with the outer values. If not, it may not be and
* we must retry. Typically this loop runs only once; occasionally
* it runs twice, and only rarely does it run longer.
* (Using this loop avoids the need to block interrupts.)
clk
= (volatile struct clkreg
*)clkstd
[0];
} while (l
!= l2
|| h
!= h2
|| u
!= u2
|| s
!= s2
);
* Pending interrupt means that the counter wrapped and we did not
* take the interrupt yet (can only happen if clock interrupts are
* blocked). If so, add one tick. Then in any case, add remaining
* count. This should leave u < 2 seconds, since we can add at most
* two clock intervals (assuming hz > 2!).
u
+= clkint
- ((h
<< 8) | l
);
if (u
>= 1000000) { /* normalize */
* Initialize the time of day register, based on the time base which is, e.g.
u_long timbuf
= base
; /* assume no battery clock exists */
static int bbcinited
= 0;
if (badbaddr(&BBCADDR
->hil_stat
))
printf("WARNING: no battery clock\n");
* bbc_to_gmt converts and stores the gmt in timbuf.
* If an error is detected in bbc_to_gmt, or if the filesystem
* time is more recent than the gmt time in the clock,
* then use the filesystem time and warn the user.
if (!bbc_to_gmt(&timbuf
) || timbuf
< base
) {
printf("WARNING: bad date in battery clock\n");
printf("WARNING: preposterous time in file system");
timbuf
= 6*SECYR
+ 186*SECDAY
+ SECDAY
/2;
printf(" -- CHECK AND RESET THE DATE!\n");
/* Battery clock does not store usec's, so forget about it. */
* Restore the time of day hardware after a time change.
register struct bbc_tm
*tmptr
;
tmptr
= gmt_to_bbc(time
.tv_sec
);
decimal_to_bbc(0, 1, tmptr
->tm_sec
);
decimal_to_bbc(2, 3, tmptr
->tm_min
);
decimal_to_bbc(4, 5, tmptr
->tm_hour
);
decimal_to_bbc(7, 8, tmptr
->tm_mday
);
decimal_to_bbc(9, 10, tmptr
->tm_mon
);
decimal_to_bbc(11, 12, tmptr
->tm_year
);
/* Some bogusness to deal with seemingly broken hardware. Nonsense */
bbc_registers
[5] = ((tmptr
->tm_hour
/ 10) & 0x03) + 8;
write_bbc_reg(15, 13); /* reset prescalar */
for (i
= 0; i
<= NUM_BBC_REGS
; i
++)
if (bbc_registers
[i
] != write_bbc_reg(i
, bbc_registers
[i
])) {
printf("Cannot set battery backed clock\n");
/* Hours, minutes, seconds are easy */
rt
.tm_min
= (hms
% 3600) / 60;
rt
.tm_sec
= (hms
% 3600) % 60;
/* Number of years in days */
for (i
= STARTOFTIME
- 1900; day
>= days_in_year(i
); i
++)
/* Number of months in days left */
if (leapyear(rt
.tm_year
))
days_in_month(FEBRUARY
) = 29;
for (i
= 1; day
>= days_in_month(i
); i
++)
days_in_month(FEBRUARY
) = 28;
/* Days are what is left over (+1) from all that. */
int year
, month
, day
, hour
, min
, sec
;
sec
= bbc_to_decimal(1, 0);
min
= bbc_to_decimal(3, 2);
* Hours are different for some reason. Makes no sense really.
hour
= ((bbc_registers
[5] & 0x03) * 10) + bbc_registers
[4];
day
= bbc_to_decimal(8, 7);
month
= bbc_to_decimal(10, 9);
year
= bbc_to_decimal(12, 11) + 1900;
range_test(month
, 1, 12);
range_test(year
, STARTOFTIME
, 2000);
for (i
= STARTOFTIME
; i
< year
; i
++)
if (leapyear(year
) && month
> FEBRUARY
)
for (i
= 1; i
< month
; i
++)
tmp
= ((tmp
* 24 + hour
) * 60 + min
) * 60 + sec
;
register int i
, read_okay
;
for (i
= 0; i
<= NUM_BBC_REGS
; i
++)
bbc_registers
[i
] = read_bbc_reg(i
);
for (i
= 0; i
<= NUM_BBC_REGS
; i
++)
if (bbc_registers
[i
] != read_bbc_reg(i
))
send_hil_cmd(bbcaddr
, BBC_SET_REG
, &data
, 1, NULL
);
send_hil_cmd(bbcaddr
, BBC_READ_REG
, NULL
, 0, &data
);
tmp
= (u_char
) ((data
<< HIL_SSHIFT
) | reg
);
send_hil_cmd(bbcaddr
, BBC_SET_REG
, &tmp
, 1, NULL
);
send_hil_cmd(bbcaddr
, BBC_WRITE_REG
, NULL
, 0, NULL
);
send_hil_cmd(bbcaddr
, BBC_READ_REG
, NULL
, 0, &tmp
);