1 /* $NetBSD: mkclock.c,v 1.4 2002/10/02 16:02:11 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 * 3. All advertising materials mentioning features or use of this software 19 * must display the following acknowledgement: 20 * This product includes software developed by the NetBSD 21 * Foundation, Inc. and its contributors. 22 * 4. Neither the name of The NetBSD Foundation nor the names of its 23 * contributors may be used to endorse or promote products derived 24 * from this software without specific prior written permission. 25 * 26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 36 * POSSIBILITY OF SUCH DAMAGE. 37 */ 38 39 /* 40 * time-of-day clock driver for sparc machines with the MOSTEK MK48Txx. 41 */ 42 #include "opt_sparc_arch.h" 43 44 #include <sys/param.h> 45 #include <sys/kernel.h> 46 #include <sys/device.h> 47 #include <sys/systm.h> 48 49 #include <uvm/uvm_extern.h> 50 51 #include <machine/bus.h> 52 #include <machine/autoconf.h> 53 #include <machine/eeprom.h> 54 #include <machine/cpu.h> 55 #include <machine/idprom.h> 56 57 #include <dev/clock_subr.h> 58 #include <dev/ic/mk48txxreg.h> 59 60 /* Location and size of the MK48xx TOD clock, if present */ 61 static bus_space_handle_t mk_nvram_base; 62 static bus_size_t mk_nvram_size; 63 64 static int mk_clk_wenable(todr_chip_handle_t, int); 65 static int mk_nvram_wenable(int); 66 67 static int clockmatch_mainbus (struct device *, struct cfdata *, void *); 68 static int clockmatch_obio(struct device *, struct cfdata *, void *); 69 static void clockattach_mainbus(struct device *, struct device *, void *); 70 static void clockattach_obio(struct device *, struct device *, void *); 71 72 static void clockattach(int, bus_space_tag_t, bus_space_handle_t); 73 74 CFATTACH_DECL(clock_mainbus, sizeof(struct device), 75 clockmatch_mainbus, clockattach_mainbus, NULL, NULL); 76 77 CFATTACH_DECL(clock_obio, sizeof(struct device), 78 clockmatch_obio, clockattach_obio, NULL, NULL); 79 80 /* Imported from clock.c: */ 81 extern todr_chip_handle_t todr_handle; 82 extern int (*eeprom_nvram_wenable)(int); 83 void establish_hostid(struct idprom *); 84 85 86 /* 87 * The OPENPROM calls the clock the "eeprom", so we have to have our 88 * own special match function to call it the "clock". 89 */ 90 static int 91 clockmatch_mainbus(parent, cf, aux) 92 struct device *parent; 93 struct cfdata *cf; 94 void *aux; 95 { 96 struct mainbus_attach_args *ma = aux; 97 98 return (strcmp("eeprom", ma->ma_name) == 0); 99 } 100 101 static int 102 clockmatch_obio(parent, cf, aux) 103 struct device *parent; 104 struct cfdata *cf; 105 void *aux; 106 { 107 union obio_attach_args *uoba = aux; 108 struct obio4_attach_args *oba; 109 110 if (uoba->uoba_isobio4 == 0) 111 return (strcmp("eeprom", uoba->uoba_sbus.sa_name) == 0); 112 113 if (!CPU_ISSUN4) { 114 printf("clockmatch_obio: attach args mixed up\n"); 115 return (0); 116 } 117 118 /* Only these sun4s have "clock" (others have "oclock") */ 119 if (cpuinfo.cpu_type != CPUTYP_4_300 && 120 cpuinfo.cpu_type != CPUTYP_4_400) 121 return (0); 122 123 /* Make sure there is something there */ 124 oba = &uoba->uoba_oba4; 125 return (bus_space_probe(oba->oba_bustag, oba->oba_paddr, 126 1, /* probe size */ 127 0, /* offset */ 128 0, /* flags */ 129 NULL, NULL)); 130 } 131 132 /* ARGSUSED */ 133 static void 134 clockattach_mainbus(parent, self, aux) 135 struct device *parent, *self; 136 void *aux; 137 { 138 struct mainbus_attach_args *ma = aux; 139 bus_space_tag_t bt = ma->ma_bustag; 140 bus_space_handle_t bh; 141 142 /* 143 * We ignore any existing virtual address as we need to map 144 * this read-only and make it read-write only temporarily, 145 * whenever we read or write the clock chip. The clock also 146 * contains the ID ``PROM'', and I have already had the pleasure 147 * of reloading the cpu type, Ethernet address, etc, by hand from 148 * the console FORTH interpreter. I intend not to enjoy it again. 149 */ 150 if (bus_space_map(bt, 151 ma->ma_paddr, 152 ma->ma_size, 153 BUS_SPACE_MAP_LINEAR, 154 &bh) != 0) { 155 printf("%s: can't map register\n", self->dv_xname); 156 return; 157 } 158 159 clockattach(ma->ma_node, bt, bh); 160 } 161 162 static void 163 clockattach_obio(parent, self, aux) 164 struct device *parent, *self; 165 void *aux; 166 { 167 union obio_attach_args *uoba = aux; 168 bus_space_tag_t bt; 169 bus_space_handle_t bh; 170 int node; 171 172 if (uoba->uoba_isobio4 == 0) { 173 /* sun4m clock at obio */ 174 struct sbus_attach_args *sa = &uoba->uoba_sbus; 175 176 node = sa->sa_node; 177 bt = sa->sa_bustag; 178 if (sbus_bus_map(bt, 179 sa->sa_slot, sa->sa_offset, sa->sa_size, 180 BUS_SPACE_MAP_LINEAR, &bh) != 0) { 181 printf("%s: can't map register\n", self->dv_xname); 182 return; 183 } 184 } else { 185 /* sun4 clock at obio */ 186 struct obio4_attach_args *oba = &uoba->uoba_oba4; 187 188 /* 189 * Sun4's only have mk48t02 clocks, so we hard-code 190 * the device address space length to 2048. 191 */ 192 node = 0; 193 bt = oba->oba_bustag; 194 if (bus_space_map(bt, 195 oba->oba_paddr, 196 2048, /* size */ 197 BUS_SPACE_MAP_LINEAR, /* flags */ 198 &bh) != 0) { 199 printf("%s: can't map register\n", self->dv_xname); 200 return; 201 } 202 } 203 204 clockattach(node, bt, bh); 205 } 206 207 static void 208 clockattach(node, bt, bh) 209 int node; 210 bus_space_tag_t bt; 211 bus_space_handle_t bh; 212 { 213 struct idprom *idp; 214 char *model; 215 216 if (CPU_ISSUN4) 217 model = "mk48t02"; /* Hard-coded sun4 clock */ 218 else if (node != 0) 219 model = PROM_getpropstring(node, "model"); 220 else 221 panic("clockattach: node == 0"); 222 223 /* Our TOD clock year 0 represents 1968 */ 224 todr_handle = mk48txx_attach(bt, bh, model, 1968, NULL, NULL); 225 if (todr_handle == NULL) 226 panic("Cannot attach %s tod clock", model); 227 228 /* 229 * Store NVRAM base address and size in globals for use 230 * by mk_nvram_wenable(). 231 */ 232 mk_nvram_base = bh; 233 if (mk48txx_get_nvram_size(todr_handle, &mk_nvram_size) != 0) 234 panic("Cannot get nvram size on %s", model); 235 236 /* Establish clock write-enable method */ 237 todr_handle->todr_setwen = mk_clk_wenable; 238 239 #if defined(SUN4) 240 if (CPU_ISSUN4) { 241 extern struct idprom sun4_idprom_store; 242 idp = &sun4_idprom_store; 243 if (cpuinfo.cpu_type == CPUTYP_4_300 || 244 cpuinfo.cpu_type == CPUTYP_4_400) { 245 eeprom_va = bus_space_vaddr(bt, bh); 246 eeprom_nvram_wenable = mk_nvram_wenable; 247 } 248 } else 249 #endif 250 { 251 /* 252 * Location of IDPROM relative to the end of the NVRAM area 253 */ 254 #define MK48TXX_IDPROM_OFFSET (mk_nvram_size - 40) 255 256 idp = (struct idprom *)((u_long)bh + MK48TXX_IDPROM_OFFSET); 257 } 258 259 establish_hostid(idp); 260 } 261 262 /* 263 * Write en/dis-able TOD clock registers. This is a safety net to 264 * save idprom (part of mk48txx TOD clock) from being accidentally 265 * stomped on by a buggy code. We coordinate so that several writers 266 * can run simultaneously. 267 */ 268 int 269 mk_clk_wenable(handle, onoff) 270 todr_chip_handle_t handle; 271 int onoff; 272 { 273 /* XXX - we ignore `handle' here... */ 274 return (mk_nvram_wenable(onoff)); 275 } 276 277 int 278 mk_nvram_wenable(onoff) 279 { 280 int s; 281 vm_prot_t prot;/* nonzero => change prot */ 282 int npages; 283 vaddr_t base; 284 static int writers; 285 286 s = splhigh(); 287 if (onoff) 288 prot = writers++ == 0 ? VM_PROT_READ|VM_PROT_WRITE : 0; 289 else 290 prot = --writers == 0 ? VM_PROT_READ : 0; 291 splx(s); 292 293 npages = round_page((vsize_t)mk_nvram_size) << PAGE_SHIFT; 294 base = trunc_page((vaddr_t)mk_nvram_base); 295 if (prot) 296 pmap_changeprot(pmap_kernel(), base, prot, npages); 297 298 return (0); 299 } 300