1/* $NetBSD: mcclock.c,v 1.23 2024/03/06 07:34:11 thorpej Exp $ */ 2 3/*- 4 * Copyright (c) 2024 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Jason R. Thorpe. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32/* 33 * Copyright (c) 1994, 1995, 1996 Carnegie-Mellon University. 34 * All rights reserved. 35 * 36 * Author: Chris G. Demetriou 37 * 38 * Permission to use, copy, modify and distribute this software and 39 * its documentation is hereby granted, provided that both the copyright 40 * notice and this permission notice appear in all copies of the 41 * software, derivative works or modified versions, and any portions 42 * thereof, and that both notices appear in supporting documentation. 43 * 44 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" 45 * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND 46 * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. 47 * 48 * Carnegie Mellon requests users of this software to return to 49 * 50 * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU 51 * School of Computer Science 52 * Carnegie Mellon University 53 * Pittsburgh PA 15213-3890 54 * 55 * any improvements or extensions that they make and grant Carnegie the 56 * rights to redistribute these changes. 57 */ 58 59#include <sys/cdefs.h> /* RCS ID & Copyright macro defns */ 60 61__KERNEL_RCSID(0, "$NetBSD: mcclock.c,v 1.23 2024/03/06 07:34:11 thorpej Exp $"); 62 63#include "opt_clock_compat_osf1.h" 64#include "opt_multiprocessor.h" 65 66#include <sys/param.h> 67#include <sys/bus.h> 68#include <sys/cpu.h> 69#include <sys/device.h> 70#include <sys/kernel.h> 71#include <sys/lwp.h> 72#include <sys/systm.h> 73#if defined(MULTIPROCESSOR) 74#include <sys/xcall.h> 75#endif 76 77#include <machine/cpu_counter.h> 78 79#include <dev/clock_subr.h> 80 81#include <dev/ic/mc146818reg.h> 82#include <dev/ic/mc146818var.h> 83 84#include <alpha/alpha/mcclockvar.h> 85#include <alpha/alpha/clockvar.h> 86 87#ifdef CLOCK_COMPAT_OSF1 88/* 89 * According to OSF/1's /usr/sys/include/arch/alpha/clock.h, 90 * the console adjusts the RTC years 13..19 to 93..99 and 91 * 20..40 to 00..20. (historical reasons?) 92 * DEC Unix uses an offset to the year to stay outside 93 * the dangerous area for the next couple of years. 94 */ 95#define UNIX_YEAR_OFFSET 52 /* 41=>1993, 12=>2064 */ 96#else 97#define UNIX_YEAR_OFFSET 0 98#endif 99 100static void mcclock_set_pcc_freq(struct mc146818_softc *); 101static void mcclock_init(void *); 102 103#if defined(MULTIPROCESSOR) 104struct mcclock_trampoline_arg { 105 todr_chip_handle_t handle; /* IN */ 106 struct clock_ymdhms *dt; /* IN */ 107 int rv; /* OUT */ 108}; 109 110static void 111mcclock_trampoline(void *arg1, void *arg2) 112{ 113 int (*func)(todr_chip_handle_t, struct clock_ymdhms *) = arg1; 114 struct mcclock_trampoline_arg *arg = arg2; 115 116 arg->rv = (*func)(arg->handle, arg->dt); 117} 118 119static int 120mcclock_bounce(int (*func)(todr_chip_handle_t, struct clock_ymdhms *), 121 struct mcclock_trampoline_arg *arg) 122{ 123 /* 124 * If we're not on the primary CPU, then we need to make 125 * a cross-call to the primary to access the clock registers. 126 * But we do a little work to avoid even calling into the 127 * cross-call code if we can avoid it. 128 */ 129 int bound = curlwp_bind(); 130 131 if (CPU_IS_PRIMARY(curcpu())) { 132 mcclock_trampoline(func, arg); 133 curlwp_bindx(bound); 134 } else { 135 curlwp_bindx(bound); 136 uint64_t token = xc_unicast(0, mcclock_trampoline, 137 func, arg, &cpu_info_primary); 138 xc_wait(token); 139 } 140 return arg->rv; 141} 142 143static int 144mcclock_gettime_ymdhms(todr_chip_handle_t handle, struct clock_ymdhms *dt) 145{ 146 struct mcclock_trampoline_arg arg = { 147 .handle = handle, 148 .dt = dt, 149 }; 150 151 return mcclock_bounce(mc146818_gettime_ymdhms, &arg); 152} 153 154static int 155mcclock_settime_ymdhms(todr_chip_handle_t handle, struct clock_ymdhms *dt) 156{ 157 struct mcclock_trampoline_arg arg = { 158 .handle = handle, 159 .dt = dt, 160 }; 161 162 return mcclock_bounce(mc146818_settime_ymdhms, &arg); 163} 164#endif /* MULTIPROCESSOR */ 165 166void 167mcclock_attach(struct mcclock_softc *msc) 168{ 169 struct mc146818_softc *sc = &msc->sc_mc146818; 170 171 KASSERT(cold); 172 KASSERT(CPU_IS_PRIMARY(curcpu())); 173 174 sc->sc_year0 = 1900 + UNIX_YEAR_OFFSET; 175 sc->sc_flag = 0; /* BINARY, 24HR */ 176 177#if defined(MULTIPROCESSOR) 178 /* 179 * Because some Alpha systems have clocks that can only be 180 * accessed by the primary CPU, we need to wrap the mc146818 181 * gettime/settime routines if we have such a clock. 182 */ 183 if (msc->sc_primary_only) { 184 sc->sc_handle.todr_gettime_ymdhms = mcclock_gettime_ymdhms; 185 sc->sc_handle.todr_settime_ymdhms = mcclock_settime_ymdhms; 186 } 187#endif 188 189 mc146818_attach(sc); 190 191 aprint_normal("\n"); 192 193 /* Turn interrupts off, just in case. */ 194 (*sc->sc_mcwrite)(sc, MC_REGB, MC_REGB_BINARY | MC_REGB_24HR); 195 196 mcclock_set_pcc_freq(sc); 197 198 clockattach(mcclock_init, (void *)sc); 199} 200 201#define NLOOP 4 202 203static void 204mcclock_set_pcc_freq(struct mc146818_softc *sc) 205{ 206 struct cpu_info *ci; 207 uint64_t freq; 208 uint32_t ctrdiff[NLOOP], pcc_start, pcc_end; 209 uint8_t reg_a; 210 int i; 211 212 KASSERT(cold); 213 KASSERT(CPU_IS_PRIMARY(curcpu())); 214 215 /* save REG_A */ 216 reg_a = (*sc->sc_mcread)(sc, MC_REGA); 217 218 /* set interval 16Hz to measure pcc */ 219 (*sc->sc_mcwrite)(sc, MC_REGA, MC_BASE_32_KHz | MC_RATE_16_Hz); 220 221 /* clear interrupt flags */ 222 (void)(*sc->sc_mcread)(sc, MC_REGC); 223 224 /* Run the loop an extra time to prime the cache. */ 225 for (i = 0; i < NLOOP; i++) { 226 227 /* wait till the periodic interrupt flag is set */ 228 while (((*sc->sc_mcread)(sc, MC_REGC) & MC_REGC_PF) == 0) 229 ; 230 pcc_start = cpu_counter32(); 231 232 /* wait till the periodic interrupt flag is set again */ 233 while (((*sc->sc_mcread)(sc, MC_REGC) & MC_REGC_PF) == 0) 234 ; 235 pcc_end = cpu_counter32(); 236 237 ctrdiff[i] = pcc_end - pcc_start; 238 } 239 240 freq = ((ctrdiff[NLOOP - 2] + ctrdiff[NLOOP - 1]) * 16 /* Hz */) / 2; 241 242 /* restore REG_A */ 243 (*sc->sc_mcwrite)(sc, MC_REGA, reg_a); 244 245 /* XXX assume all processors have the same clock and frequency */ 246 for (ci = &cpu_info_primary; ci; ci = ci->ci_next) 247 ci->ci_pcc_freq = freq; 248} 249 250static void 251mcclock_init(void *dev) 252{ 253 struct mc146818_softc *sc = dev; 254 255 kpreempt_disable(); 256 257 /* 258 * The Alpha port calls clock init function is called on each CPU, 259 * but for this clock, we only want to do work on the primary. 260 */ 261 if (CPU_IS_PRIMARY(curcpu())) { 262 /* enable interval clock interrupt */ 263 (*sc->sc_mcwrite)(sc, MC_REGA, 264 MC_BASE_32_KHz | MC_RATE_1024_Hz); 265 (*sc->sc_mcwrite)(sc, MC_REGB, 266 MC_REGB_PIE | MC_REGB_SQWE | MC_REGB_BINARY | MC_REGB_24HR); 267 } 268 269 kpreempt_enable(); 270} 271