1 /* $NetBSD: mkclock.c,v 1.21 2025/09/07 21:45:15 thorpej Exp $ */ 2 3 /*- 4 * Copyright (c) 2002 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Paul Kranenburg. 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 * time-of-day clock driver for sparc machines with the MOSTEK MK48Txx. 34 */ 35 36 #include <sys/cdefs.h> 37 __KERNEL_RCSID(0, "$NetBSD: mkclock.c,v 1.21 2025/09/07 21:45:15 thorpej Exp $"); 38 39 #include "opt_sparc_arch.h" 40 41 #include <sys/param.h> 42 #include <sys/kernel.h> 43 #include <sys/device.h> 44 #include <sys/systm.h> 45 46 #include <uvm/uvm_extern.h> 47 48 #include <sys/bus.h> 49 #include <machine/autoconf.h> 50 #include <machine/eeprom.h> 51 #include <machine/promlib.h> 52 #include <machine/cpu.h> 53 54 #include <sparc/dev/bootbusvar.h> 55 #include <sparc/sparc/timervar.h> 56 57 #include <dev/clock_subr.h> 58 #include <dev/ic/mk48txxreg.h> 59 #include <dev/ic/mk48txxvar.h> 60 61 /* Location and size of the MK48xx TOD clock, if present */ 62 static bus_space_handle_t mk_nvram_base; 63 static bus_size_t mk_nvram_size; 64 65 static int mk_clk_wenable(todr_chip_handle_t, int); 66 static int mk_nvram_wenable(int); 67 68 static int clockmatch_mainbus(device_t, cfdata_t, void *); 69 static int clockmatch_obio(device_t, cfdata_t, void *); 70 static int clockmatch_bootbus(device_t, cfdata_t, void *); 71 static void clockattach_mainbus(device_t, device_t, void *); 72 static void clockattach_obio(device_t, device_t, void *); 73 static void clockattach_bootbus(device_t, device_t, void *); 74 75 static void clockattach(struct mk48txx_softc *, int); 76 77 CFATTACH_DECL_NEW(clock_mainbus, sizeof(struct mk48txx_softc), 78 clockmatch_mainbus, clockattach_mainbus, NULL, NULL); 79 80 CFATTACH_DECL_NEW(clock_obio, sizeof(struct mk48txx_softc), 81 clockmatch_obio, clockattach_obio, NULL, NULL); 82 83 CFATTACH_DECL_NEW(clock_bootbus, sizeof(struct mk48txx_softc), 84 clockmatch_bootbus, clockattach_bootbus, NULL, NULL); 85 86 87 /* 88 * The OPENPROM calls the clock the "eeprom", so we have to have our 89 * own special match function to call it the "clock". 90 */ 91 static int 92 clockmatch_mainbus(device_t parent, cfdata_t cf, void *aux) 93 { 94 struct mainbus_attach_args *ma = aux; 95 96 return (strcmp("eeprom", ma->ma_name) == 0); 97 } 98 99 static int 100 clockmatch_obio(device_t parent, cfdata_t cf, void *aux) 101 { 102 union obio_attach_args *uoba = aux; 103 struct obio4_attach_args *oba; 104 105 if (uoba->uoba_isobio4 == 0) 106 return (strcmp("eeprom", uoba->uoba_sbus.sa_name) == 0); 107 108 if (!CPU_ISSUN4) { 109 printf("clockmatch_obio: attach args mixed up\n"); 110 return (0); 111 } 112 113 /* Only these sun4s have "clock" (others have "oclock") */ 114 if (cpuinfo.cpu_type != CPUTYP_4_300 && 115 cpuinfo.cpu_type != CPUTYP_4_400) 116 return (0); 117 118 /* Make sure there is something there */ 119 oba = &uoba->uoba_oba4; 120 return (bus_space_probe(oba->oba_bustag, oba->oba_paddr, 121 1, /* probe size */ 122 0, /* offset */ 123 0, /* flags */ 124 NULL, NULL)); 125 } 126 127 static int 128 clockmatch_bootbus(device_t parent, cfdata_t cf, void *aux) 129 { 130 struct bootbus_attach_args *baa = aux; 131 132 return (strcmp("eeprom", baa->ba_name) == 0); 133 } 134 135 /* ARGSUSED */ 136 static void 137 clockattach_mainbus(device_t parent, device_t self, void *aux) 138 { 139 struct mk48txx_softc *sc = device_private(self); 140 struct mainbus_attach_args *ma = aux; 141 142 sc->sc_dev = self; 143 sc->sc_bst = ma->ma_bustag; 144 145 /* 146 * We ignore any existing virtual address as we need to map 147 * this read-only and make it read-write only temporarily, 148 * whenever we read or write the clock chip. The clock also 149 * contains the ID ``PROM'', and I have already had the pleasure 150 * of reloading the CPU type, Ethernet address, etc, by hand from 151 * the console FORTH interpreter. I intend not to enjoy it again. 152 */ 153 if (bus_space_map(sc->sc_bst, 154 ma->ma_paddr, 155 ma->ma_size, 156 BUS_SPACE_MAP_LINEAR, 157 &sc->sc_bsh) != 0) { 158 aprint_error(": can't map register\n"); 159 return; 160 } 161 162 clockattach(sc, ma->ma_node); 163 } 164 165 static void 166 clockattach_obio(device_t parent, device_t self, void *aux) 167 { 168 struct mk48txx_softc *sc = device_private(self); 169 union obio_attach_args *uoba = aux; 170 int node; 171 172 sc->sc_dev = self; 173 174 if (uoba->uoba_isobio4 == 0) { 175 /* sun4m clock at obio */ 176 struct sbus_attach_args *sa = &uoba->uoba_sbus; 177 178 node = sa->sa_node; 179 sc->sc_bst = sa->sa_bustag; 180 if (sbus_bus_map(sc->sc_bst, 181 sa->sa_slot, sa->sa_offset, sa->sa_size, 182 BUS_SPACE_MAP_LINEAR, &sc->sc_bsh) != 0) { 183 aprint_error(": can't map register\n"); 184 return; 185 } 186 } else { 187 /* sun4 clock at obio */ 188 struct obio4_attach_args *oba = &uoba->uoba_oba4; 189 190 /* 191 * Sun4's only have mk48t02 clocks, so we hard-code 192 * the device address space length to 2048. 193 */ 194 node = 0; 195 sc->sc_bst = oba->oba_bustag; 196 if (bus_space_map(sc->sc_bst, 197 oba->oba_paddr, 198 2048, /* size */ 199 BUS_SPACE_MAP_LINEAR, /* flags */ 200 &sc->sc_bsh) != 0) { 201 aprint_error(": can't map register\n"); 202 return; 203 } 204 } 205 206 clockattach(sc, node); 207 } 208 209 static void 210 clockattach_bootbus(device_t parent, device_t self, void *aux) 211 { 212 struct mk48txx_softc *sc = device_private(self); 213 struct bootbus_attach_args *baa = aux; 214 215 sc->sc_bst = baa->ba_bustag; 216 sc->sc_dev = self; 217 218 if (bus_space_map(sc->sc_bst, 219 BUS_ADDR(baa->ba_reg[0].oa_space, 220 baa->ba_reg[0].oa_base), 221 baa->ba_reg[0].oa_size, 222 BUS_SPACE_MAP_LINEAR, 223 &sc->sc_bsh) != 0) { 224 aprint_error(": can't map register\n"); 225 return; 226 } 227 228 clockattach(sc, baa->ba_node); 229 } 230 231 static void 232 clockattach(struct mk48txx_softc *sc, int node) 233 { 234 235 if (CPU_ISSUN4) 236 sc->sc_model = "mk48t02"; /* Hard-coded sun4 clock */ 237 else if (node != 0) 238 sc->sc_model = prom_getpropstring(node, "model"); 239 else 240 panic("clockattach: node == 0"); 241 242 /* Our TOD clock year 0 represents 1968 */ 243 sc->sc_year0 = 1968; 244 mk48txx_attach(sc); 245 246 aprint_normal("\n"); 247 248 /* 249 * Store NVRAM base address and size in globals for use 250 * by mk_nvram_wenable(). 251 */ 252 mk_nvram_base = sc->sc_bsh; 253 if (mk48txx_get_nvram_size(&sc->sc_handle, &mk_nvram_size) != 0) 254 panic("Cannot get nvram size on %s", sc->sc_model); 255 256 /* Establish clock write-enable method */ 257 sc->sc_handle.todr_setwen = mk_clk_wenable; 258 259 #if defined(SUN4) 260 if (CPU_ISSUN4) { 261 if (cpuinfo.cpu_type == CPUTYP_4_300 || 262 cpuinfo.cpu_type == CPUTYP_4_400) { 263 eeprom_va = bus_space_vaddr(sc->sc_bst, sc->sc_bsh); 264 eeprom_nvram_wenable = mk_nvram_wenable; 265 } 266 } 267 #endif 268 } 269 270 /* 271 * Write en/dis-able TOD clock registers. This is a safety net to 272 * save idprom (part of mk48txx TOD clock) from being accidentally 273 * stomped on by a buggy code. We coordinate so that several writers 274 * can run simultaneously. 275 */ 276 int 277 mk_clk_wenable(todr_chip_handle_t handle, int onoff) 278 { 279 280 /* XXX - we ignore `handle' here... */ 281 return (mk_nvram_wenable(onoff)); 282 } 283 284 int 285 mk_nvram_wenable(int onoff) 286 { 287 int s; 288 vm_prot_t prot;/* nonzero => change prot */ 289 vaddr_t base; 290 vsize_t size; 291 static int writers; 292 293 s = splhigh(); 294 if (onoff) 295 prot = writers++ == 0 ? VM_PROT_READ|VM_PROT_WRITE : 0; 296 else 297 prot = --writers == 0 ? VM_PROT_READ : 0; 298 splx(s); 299 300 size = round_page((vsize_t)mk_nvram_size); 301 base = trunc_page((vaddr_t)mk_nvram_base); 302 if (prot) 303 pmap_kprotect(base, size, prot); 304 305 return (0); 306 } 307