1 /* $NetBSD: oclock.c,v 1.19 2010/01/03 23:03:21 mrg 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 * sun4 intersil time-of-day clock driver. This chip also provides 34 * the system timer. 35 * 36 * Only 4/100's and 4/200's have this old clock device. 37 */ 38 39 #include <sys/cdefs.h> 40 __KERNEL_RCSID(0, "$NetBSD: oclock.c,v 1.19 2010/01/03 23:03:21 mrg Exp $"); 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 <machine/bus.h> 50 #include <machine/promlib.h> 51 #include <machine/autoconf.h> 52 53 #include <dev/clock_subr.h> 54 #include <dev/ic/intersil7170reg.h> 55 #include <dev/ic/intersil7170var.h> 56 57 /* Imported from clock.c: */ 58 extern int oldclk; 59 extern int timerblurb; 60 extern void (*timer_init)(void); 61 62 63 static int oclockmatch(device_t, cfdata_t, void *); 64 static void oclockattach(device_t, device_t, void *); 65 66 CFATTACH_DECL_NEW(oclock, sizeof(struct intersil7170_softc), 67 oclockmatch, oclockattach, NULL, NULL); 68 69 #if defined(SUN4) 70 static bus_space_tag_t i7_bt; 71 static bus_space_handle_t i7_bh; 72 73 #define intersil_disable() \ 74 bus_space_write_1(i7_bt, i7_bh, INTERSIL_ICMD, \ 75 INTERSIL_COMMAND(INTERSIL_CMD_RUN, INTERSIL_CMD_IDISABLE)); 76 77 #define intersil_enable() \ 78 bus_space_write_1(i7_bt, i7_bh, INTERSIL_ICMD, \ 79 INTERSIL_COMMAND(INTERSIL_CMD_RUN, INTERSIL_CMD_IENABLE)); 80 81 #define intersil_clear() bus_space_read_1(i7_bt, i7_bh, INTERSIL_IINTR) 82 83 int oclockintr(void *); 84 static struct intrhand level10 = { oclockintr }; 85 void oclock_init(void); 86 #endif /* SUN4 */ 87 88 /* 89 * old clock match routine 90 */ 91 static int 92 oclockmatch(device_t parent, cfdata_t cf, void *aux) 93 { 94 union obio_attach_args *uoba = aux; 95 struct obio4_attach_args *oba; 96 97 if (uoba->uoba_isobio4 == 0) 98 return (0); 99 100 /* Only these sun4s have oclock */ 101 if (!CPU_ISSUN4 || 102 (cpuinfo.cpu_type != CPUTYP_4_100 && 103 cpuinfo.cpu_type != CPUTYP_4_200)) 104 return (0); 105 106 /* Make sure there is something there */ 107 oba = &uoba->uoba_oba4; 108 return (bus_space_probe(oba->oba_bustag, oba->oba_paddr, 109 1, /* probe size */ 110 0, /* offset */ 111 0, /* flags */ 112 NULL, NULL)); 113 } 114 115 /* ARGSUSED */ 116 static void 117 oclockattach(device_t parent, device_t self, void *aux) 118 { 119 #if defined(SUN4) 120 struct intersil7170_softc *sc = device_private(self); 121 union obio_attach_args *uoba = aux; 122 struct obio4_attach_args *oba = &uoba->uoba_oba4; 123 124 oldclk = 1; /* we've got an oldie! */ 125 126 sc->sc_dev = self; 127 sc->sc_bst = oba->oba_bustag; 128 if (bus_space_map(sc->sc_bst, 129 oba->oba_paddr, 130 sizeof(struct intersil7170), 131 BUS_SPACE_MAP_LINEAR, /* flags */ 132 &sc->sc_bsh) != 0) { 133 aprint_error(": can't map register\n"); 134 return; 135 } 136 i7_bt = sc->sc_bst; 137 i7_bh = sc->sc_bsh; 138 139 /* 140 * calibrate delay() 141 */ 142 ienab_bic(IE_L14 | IE_L10); /* disable all clock intrs */ 143 for (timerblurb = 1; ; timerblurb++) { 144 int ival; 145 146 /* Set to 1/100 second interval */ 147 bus_space_write_1(sc->sc_bst, sc->sc_bsh, INTERSIL_IINTR, 148 INTERSIL_INTER_CSECONDS); 149 150 /* enable clock */ 151 intersil_enable(); 152 153 while ((intersil_clear() & INTERSIL_INTER_PENDING) == 0) 154 /* sync with interrupt */; 155 while ((intersil_clear() & INTERSIL_INTER_PENDING) == 0) 156 /* XXX: do it again, seems to need it */; 157 158 /* Probe 1/100 sec delay */ 159 delay(10000); 160 161 /* clear, save value */ 162 ival = intersil_clear(); 163 164 /* disable clock */ 165 intersil_disable(); 166 167 if ((ival & INTERSIL_INTER_PENDING) != 0) { 168 aprint_normal(" delay constant %d%s\n", timerblurb, 169 (timerblurb == 1) ? " [TOO SMALL?]" : ""); 170 break; 171 } 172 if (timerblurb > 10) { 173 aprint_normal("\n"); 174 aprint_error_dev(self, "calibration failing; " 175 "clamped at %d\n", timerblurb); 176 break; 177 } 178 } 179 180 timer_init = oclock_init; 181 182 /* link interrupt handler */ 183 intr_establish(10, 0, &level10, NULL, false); 184 185 /* Our TOD clock year 0 represents 1968 */ 186 sc->sc_year0 = 1968; 187 intersil7170_attach(sc); 188 189 aprint_normal("\n"); 190 #endif /* SUN4 */ 191 } 192 193 #if defined(SUN4) 194 /* 195 * Set up the real-time and statistics clocks. 196 * Leave stathz 0 only if no alternative timer is available. 197 * 198 * The frequencies of these clocks must be an even number of microseconds. 199 */ 200 void 201 oclock_init(void) 202 { 203 204 profhz = hz = 100; 205 tick = 1000000 / hz; 206 207 /* Select 1/100 second interval */ 208 bus_space_write_1(i7_bt, i7_bh, INTERSIL_IINTR, 209 INTERSIL_INTER_CSECONDS); 210 211 ienab_bic(IE_L14 | IE_L10); /* disable all clock intrs */ 212 intersil_disable(); /* disable clock */ 213 (void)intersil_clear(); /* clear interrupts */ 214 ienab_bis(IE_L10); /* enable l10 interrupt */ 215 intersil_enable(); /* enable clock */ 216 } 217 218 /* 219 * Level 10 (clock) interrupts from system counter. 220 * If we are using the FORTH PROM for console input, we need to check 221 * for that here as well, and generate a software interrupt to read it. 222 */ 223 int 224 oclockintr(void *cap) 225 { 226 int s; 227 228 /* 229 * Protect the clearing of the clock interrupt. If we don't 230 * do this, and we're interrupted (by the zs, for example), 231 * the clock stops! 232 * XXX WHY DOES THIS HAPPEN? 233 */ 234 s = splhigh(); 235 236 (void)intersil_clear(); 237 ienab_bic(IE_L10); /* clear interrupt */ 238 ienab_bis(IE_L10); /* enable interrupt */ 239 splx(s); 240 241 hardclock((struct clockframe *)cap); 242 return (1); 243 } 244 #endif /* SUN4 */ 245