Home | History | Annotate | Line # | Download | only in vax
      1 /*	$NetBSD: clock.c,v 1.61 2025/09/07 14:31:58 thorpej Exp $	 */
      2 /*
      3  * Copyright (c) 1995 Ludd, University of Lule}, Sweden.
      4  * All rights reserved.
      5  *
      6  * Redistribution and use in source and binary forms, with or without
      7  * modification, are permitted provided that the following conditions
      8  * are met:
      9  * 1. Redistributions of source code must retain the above copyright
     10  *    notice, this list of conditions and the following disclaimer.
     11  * 2. Redistributions in binary form must reproduce the above copyright
     12  *    notice, this list of conditions and the following disclaimer in the
     13  *    documentation and/or other materials provided with the distribution.
     14  *
     15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     16  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     17  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     18  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     19  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     20  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     21  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     22  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     24  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     25  */
     26 
     27 #include <sys/cdefs.h>
     28 __KERNEL_RCSID(0, "$NetBSD: clock.c,v 1.61 2025/09/07 14:31:58 thorpej Exp $");
     29 
     30 #include <sys/param.h>
     31 #include <sys/systm.h>
     32 #include <sys/cpu.h>
     33 #include <sys/device.h>
     34 #include <sys/timetc.h>
     35 #include <sys/kernel.h>
     36 
     37 #include <machine/sid.h>
     38 #include <machine/clock.h>
     39 
     40 #include "opt_cputype.h"
     41 
     42 struct evcnt clock_misscnt =
     43 	EVCNT_INITIALIZER(EVCNT_TYPE_MISC, NULL, "clock", "intr miss");
     44 
     45 EVCNT_ATTACH_STATIC(clock_misscnt);
     46 
     47 struct evcnt clock_intrcnt =
     48 	EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, "clock", "intr");
     49 
     50 EVCNT_ATTACH_STATIC(clock_intrcnt);
     51 
     52 static int vax_gettime(todr_chip_handle_t, struct timeval *);
     53 static int vax_settime(todr_chip_handle_t, struct timeval *);
     54 
     55 static struct todr_chip_handle todr_handle = {
     56 	.todr_gettime = vax_gettime,
     57 	.todr_settime = vax_settime,
     58 };
     59 
     60 #if VAX46 || VAXANY
     61 static u_int
     62 vax_diag_get_counter(struct timecounter *tc)
     63 {
     64 	extern struct vs_cpu *ka46_cpu;
     65 	int cur_hardclock;
     66 	u_int counter;
     67 
     68 	do {
     69 		cur_hardclock = getticks();
     70 		counter = *(volatile u_int *)&ka46_cpu->vc_diagtimu;
     71 	} while (cur_hardclock != getticks());
     72 
     73 	counter = (counter & 0x3ff) + (counter >> 16) * 1024;
     74 
     75 	return counter + getticks() * tick;
     76 }
     77 #endif
     78 
     79 static u_int
     80 vax_mfpr_get_counter(struct timecounter *tc)
     81 {
     82 	int cur_hardclock;
     83 	u_int counter;
     84 	static int prev_count, prev_hardclock;
     85 
     86 	do {
     87 		cur_hardclock = getticks();
     88 		counter = mfpr(PR_ICR) + tick;
     89 	} while (cur_hardclock != getticks());
     90 
     91 	/*
     92 	 * Handle interval counter wrapping with interrupts blocked.
     93 	 * If the current getticks() is less than what we saw
     94 	 *   previously, use the previous value.
     95 	 * If the interval counter is smaller, assume it has wrapped,
     96 	 *   and if the [adjusted] current hardclock ticks is the same
     97 	 *   as what we saw previously, increment the local copy of
     98 	 *   the hardclock ticks.
     99 	 */
    100 	if (cur_hardclock < prev_hardclock)
    101 		cur_hardclock = prev_hardclock;
    102 	if (counter < prev_count && cur_hardclock == prev_hardclock)
    103 		cur_hardclock++;
    104 
    105 	prev_count = counter;
    106 	prev_hardclock=cur_hardclock;
    107 
    108 	return counter + cur_hardclock * tick;
    109 }
    110 
    111 #if VAX46 || VAXANY
    112 static struct timecounter vax_diag_tc = {
    113 	.tc_get_timecount = vax_diag_get_counter,
    114 	.tc_counter_mask = ~0u,
    115 	.tc_frequency = 1000000,
    116 	.tc_name = "diagtimer",
    117 	.tc_quality = 100,
    118 };
    119 #endif
    120 
    121 static struct timecounter vax_mfpr_tc = {
    122 	.tc_get_timecount = vax_mfpr_get_counter,
    123 	.tc_counter_mask = ~0u,
    124 	.tc_frequency = 1000000,
    125 	.tc_name = "mfpr",
    126 	.tc_quality = 100,
    127 };
    128 
    129 /*
    130  * A delayloop that delays about the number of milliseconds that is
    131  * given as argument.
    132  */
    133 void
    134 delay(int i)
    135 {
    136 	__asm ("1: sobgtr %0, 1b" : : "r" (dep_call->cpu_vups * i));
    137 }
    138 
    139 /*
    140  * On all VAXen there are a microsecond clock that should
    141  * be used for interval interrupts. Some CPUs don't use the ICR interval
    142  * register but it doesn't hurt to load it anyway.
    143  */
    144 void
    145 cpu_initclocks(void)
    146 {
    147 	mtpr(-10000, PR_NICR); /* Load in count register */
    148 	mtpr(0x800000d1, PR_ICCS); /* Start clock and enable interrupt */
    149 
    150 	todr_attach(&todr_handle);
    151 
    152 #if VAX46 || VAXANY
    153 	if (vax_boardtype == VAX_BTYP_46)
    154 		tc_init(&vax_diag_tc);
    155 #endif
    156 	if (vax_boardtype != VAX_BTYP_46 && vax_boardtype != VAX_BTYP_48)
    157 		tc_init(&vax_mfpr_tc);
    158 }
    159 
    160 int
    161 vax_gettime(todr_chip_handle_t handle, struct timeval *tvp)
    162 {
    163 	tvp->tv_sec = handle->todr_base_time;
    164 	return (*dep_call->cpu_gettime)(tvp);
    165 }
    166 
    167 int
    168 vax_settime(todr_chip_handle_t handle, struct timeval *tvp)
    169 {
    170 	(*dep_call->cpu_settime)(tvp);
    171 	return 0;
    172 }
    173 
    174 /*
    175  * There are two types of real-time battery-backed up clocks on
    176  * VAX computers, one with a register that counts up every 1/100 second,
    177  * one with a clock chip that delivers time. For the register clock
    178  * we have a generic version, and for the chip clock there are
    179  * support routines for time conversion.
    180  */
    181 /*
    182  * Converts a year to corresponding number of ticks.
    183  */
    184 int
    185 yeartonum(int y)
    186 {
    187 	int n;
    188 
    189 	for (n = 0, y -= 1; y > 1969; y--)
    190  		n += days_per_year(y) * SECS_PER_DAY;
    191 	return n;
    192 }
    193 
    194 /*
    195  * Converts tick number to a year 1970 ->
    196  */
    197 int
    198 numtoyear(int num)
    199 {
    200 	int y = 1970, j;
    201 	while(num >= (j = days_per_year(y) * SECS_PER_DAY)) {
    202 		y++;
    203 		num -= j;
    204 	}
    205 	return y;
    206 }
    207 
    208 #if VAX750 || VAX780 || VAX8600 || VAX650 || \
    209     VAX660 || VAX670 || VAX680 || VAX53 || VAXANY
    210 /*
    211  * Reads the TODR register; returns a (probably) true tick value, and 0 is
    212  * success or EINVAL if failed.  The year is based on the argument
    213  * year; the TODR doesn't hold years.
    214  */
    215 int
    216 generic_gettime(struct timeval *tvp)
    217 {
    218 	unsigned klocka = mfpr(PR_TODR);
    219 
    220 	/*
    221 	 * Sanity check.
    222 	 */
    223 	if (klocka < TODRBASE) {
    224 		if (klocka == 0)
    225 			printf("TODR stopped");
    226 		else
    227 			printf("TODR too small");
    228 		return EINVAL;
    229 	}
    230 
    231 	tvp->tv_sec = yeartonum(numtoyear(tvp->tv_sec)) + (klocka - TODRBASE) / 100;
    232 	return 0;
    233 }
    234 
    235 /*
    236  * Takes the current system time and writes it to the TODR.
    237  */
    238 void
    239 generic_settime(struct timeval *tvp)
    240 {
    241 	unsigned tid = tvp->tv_sec, bastid;
    242 
    243 	bastid = tid - yeartonum(numtoyear(tid));
    244 	mtpr((bastid * 100) + TODRBASE, PR_TODR);
    245 }
    246 #endif
    247 
    248 #if VAX630 || VAX410 || VAX43 || VAX8200 || VAX46 || VAX48 || VAX49 || VAXANY
    249 
    250 volatile short *clk_page;	/* where the chip is mapped in virtual memory */
    251 int	clk_adrshift;	/* how much to multiply the in-page address with */
    252 int	clk_tweak;	/* Offset of time into word. */
    253 
    254 #define	REGPEEK(off)	(clk_page[off << clk_adrshift] >> clk_tweak)
    255 #define	REGPOKE(off, v)	(clk_page[off << clk_adrshift] = ((v) << clk_tweak))
    256 
    257 int
    258 chip_gettime(struct timeval *tvp)
    259 {
    260 	struct clock_ymdhms c;
    261 	int timeout = 1<<15, s;
    262 
    263 #ifdef DIAGNOSTIC
    264 	if (clk_page == 0)
    265 		panic("trying to use unset chip clock page");
    266 #endif
    267 
    268 	if ((REGPEEK(CSRD_OFF) & CSRD_VRT) == 0) {
    269 		printf("WARNING: TOY clock not marked valid\n");
    270 		return EINVAL;
    271 	}
    272 	while (REGPEEK(CSRA_OFF) & CSRA_UIP) {
    273 		if (--timeout == 0) {
    274 			printf ("TOY clock timed out");
    275 			return ETIMEDOUT;
    276 		}
    277 	}
    278 
    279 	s = splhigh();
    280 	c.dt_year = ((u_char)REGPEEK(YR_OFF)) + 1970;
    281 	c.dt_mon = REGPEEK(MON_OFF);
    282 	c.dt_day = REGPEEK(DAY_OFF);
    283 	c.dt_wday = REGPEEK(WDAY_OFF);
    284 	c.dt_hour = REGPEEK(HR_OFF);
    285 	c.dt_min = REGPEEK(MIN_OFF);
    286 	c.dt_sec = REGPEEK(SEC_OFF);
    287 	splx(s);
    288 
    289 	tvp->tv_sec = clock_ymdhms_to_secs(&c);
    290 	tvp->tv_usec = 0;
    291 	return 0;
    292 }
    293 
    294 void
    295 chip_settime(struct timeval *tvp)
    296 {
    297 	struct clock_ymdhms c;
    298 
    299 #ifdef DIAGNOSTIC
    300 	if (clk_page == 0)
    301 		panic("trying to use unset chip clock page");
    302 #endif
    303 
    304 	REGPOKE(CSRB_OFF, CSRB_SET);
    305 
    306 	clock_secs_to_ymdhms(tvp->tv_sec, &c);
    307 
    308 	REGPOKE(YR_OFF, ((u_char)(c.dt_year - 1970)));
    309 	REGPOKE(MON_OFF, c.dt_mon);
    310 	REGPOKE(DAY_OFF, c.dt_day);
    311 	REGPOKE(WDAY_OFF, c.dt_wday);
    312 	REGPOKE(HR_OFF, c.dt_hour);
    313 	REGPOKE(MIN_OFF, c.dt_min);
    314 	REGPOKE(SEC_OFF, c.dt_sec);
    315 
    316 	REGPOKE(CSRB_OFF, CSRB_DM|CSRB_24);
    317 };
    318 #endif
    319