* 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.8 (Berkeley) %G%
#include "../dev/hilreg.h"
#include "../include/psl.h"
#include "../include/cpu.h"
#if defined(GPROF) && defined(PROFTIMER)
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.
* Startrtclock restarts the real-time clock, which provides
* hardclock interrupts to kern_clock.c.
* Inittodr initializes the time of day hardware which provides
* Resettodr restores the time of day hardware after a time change.
* A note on the real-time clock:
* We actually load the clock with CLK_INTERVAL-1 instead of CLK_INTERVAL.
* This is because the counter decrements to zero after N+1 enabled clock
* periods where N is the value loaded into the counter.
* Start the real-time clock.
register struct clkreg
*clk
;
clkstd
[0] = IIOV(0x5F8000);
clk
= (struct clkreg
*) clkstd
[0];
clk
->clk_cr1
= CLK_RESET
;
clk
->clk_msb1
= (CLK_INTERVAL
-1) >> 8 & 0xFF;
clk
->clk_lsb1
= (CLK_INTERVAL
-1) & 0xFF;
clk
->clk_cr1
= CLK_IENAB
;
tick
= CLK_INTERVAL
* CLK_RESOLUTION
;
hz
= 1000000 / (CLK_INTERVAL
* CLK_RESOLUTION
);
* Returns number of usec since last recorded clock "tick"
* (i.e. clock interrupt).
register struct clkreg
*clk
= (struct clkreg
*) clkstd
[0];
if (high
!= clk
->clk_msb1
)
high
= (CLK_INTERVAL
-1) - ((high
<< 8) | low
);
* Pending interrupt indicates that the counter has wrapped
* since we went to splhigh(). Need to compensate.
if (clk
->clk_sr
& CLK_INT1
)
return((high
* tick
) / CLK_INTERVAL
);
* /dev/clock: mappable high resolution timer.
* This code implements a 32-bit recycling counter (with a 4 usec period)
* using timers 2 & 3 on the 6840 clock chip. The counter can be mapped
* RO into a user's address space to achieve low overhead (no system calls),
* Note that timer 3 is also used for the high precision profiling timer
* (PROFTIMER code above). Care should be taken when both uses are
* configured as only a token effort is made to avoid conflicting use.
#include "sys/resourcevar.h"
int clockon
= 0; /* non-zero if high-res timer enabled */
int profprocs
= 0; /* # of procs using profiling timer */
* Kernel profiling enabled, give up.
* If any user processes are profiling, give up.
(void) clockunmmap(dev
, (caddr_t
)0, curproc
); /* XXX */
clockioctl(dev
, cmd
, data
, flag
, p
)
error
= clockmmap(dev
, (caddr_t
*)data
, p
);
error
= clockunmmap(dev
, *(caddr_t
*)data
, p
);
*(int *)data
= CLK_RESOLUTION
;
return((off
+ (INTIOBASE
+CLKBASE
+CLKSR
-1)) >> PGSHIFT
);
flags
= MAP_FILE
|MAP_SHARED
;
*addrp
= (caddr_t
)0x1000000; /* XXX */
vn
.v_type
= VCHR
; /* XXX */
vn
.v_specinfo
= &si
; /* XXX */
vn
.v_rdev
= dev
; /* XXX */
error
= vm_mmap(&p
->p_vmspace
->vm_map
, (vm_offset_t
*)addrp
,
PAGE_SIZE
, VM_PROT_ALL
, flags
, (caddr_t
)&vn
, 0);
clockunmmap(dev
, addr
, p
)
return(EINVAL
); /* XXX: how do we deal with this? */
rv
= vm_deallocate(p
->p_vmspace
->vm_map
, (vm_offset_t
)addr
, PAGE_SIZE
);
return(rv
== KERN_SUCCESS
? 0 : EINVAL
);
register struct clkreg
*clk
= (struct clkreg
*)clkstd
[0];
clk
->clk_msb2
= -1; clk
->clk_lsb2
= -1;
clk
->clk_msb3
= -1; clk
->clk_lsb3
= -1;
clk
->clk_cr3
= CLK_OENAB
|CLK_8BIT
;
clk
->clk_cr1
= CLK_IENAB
;
register struct clkreg
*clk
= (struct clkreg
*)clkstd
[0];
clk
->clk_cr1
= CLK_IENAB
;
* This code allows the hp300 kernel to use one of the extra timers on
* the clock chip for profiling, instead of the regular system timer.
* The advantage of this is that the profiling timer can be turned up to
* a higher interrupt rate, giving finer resolution timing. The profclock
* routine is called from the lev6intr in locore, and is a specialized
* routine that calls addupc. The overhead then is far less than if
* hardclock/softclock was called. Further, the context switch code in
* locore has been changed to turn the profile clock on/off when switching
* into/out of a process that is profiling (startprofclock/stopprofclock).
* This reduces the impact of the profiling clock on other users, and might
* possibly increase the accuracy of the profiling.
int profint
= PRF_INTERVAL
; /* Clock ticks between interrupts */
int profscale
= 0; /* Scale factor from sys clock to prof clock */
char profon
= 0; /* Is profiling clock on? */
/* profon values - do not change, locore.s assumes these values */
struct proc
*p
= curproc
; /* XXX */
* If the high-res timer is running, force profiling off.
* Unfortunately, this gets reflected back to the user not as
* an error but as a lack of results.
p
->p_stats
->p_prof
.pr_scale
= 0;
* Keep track of the number of user processes that are profiling
* by checking the scale value.
* XXX: this all assumes that the profiling code is well behaved;
* i.e. profil() is called once per process with pcscale non-zero
* to turn it on, and once with pcscale zero to turn it off.
* Also assumes you don't do any forks or execs. Oh well, there
if (p
->p_stats
->p_prof
.pr_scale
)
* The profile interrupt interval must be an even divisor
* of the CLK_INTERVAL so that scaling from a system clock
* tick to a profile clock tick is possible using integer math.
if (profint
> CLK_INTERVAL
|| (CLK_INTERVAL
% profint
) != 0)
profscale
= CLK_INTERVAL
/ profint
;
register struct clkreg
*clk
= (struct clkreg
*)clkstd
[0];
clk
->clk_msb3
= (profint
-1) >> 8 & 0xFF;
clk
->clk_lsb3
= (profint
-1) & 0xFF;
clk
->clk_cr3
= CLK_IENAB
;
register struct clkreg
*clk
= (struct clkreg
*)clkstd
[0];
* profclock() is expanded in line in lev6intr() unless profiling kernel.
* Assumes it is called with clock interrupts blocked.
* If this process is being profiled record the tick.
if (curproc
->p_stats
->p_prof
.pr_scale
)
addupc(pc
, &curproc
->p_stats
->p_prof
, 1);
* Came from kernel (supervisor) mode.
* If we are profiling the kernel, record the tick.
else if (profiling
< 2) {
register int s
= pc
- s_lowpc
;
kcount
[s
/ (HISTFRACTION
* sizeof (*kcount
))]++;
* Kernel profiling was on but has been disabled.
* Mark as no longer profiling kernel and if all profiling done,
if (profiling
&& (profon
& PRF_KERNEL
)) {
* 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. */
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
);