1 /* $NetBSD: sa11x0_ost.c,v 1.24 2008/04/28 20:23:14 martin Exp $ */ 2 3 /* 4 * Copyright (c) 1997 Mark Brinicombe. 5 * Copyright (c) 1997 Causality Limited. 6 * All rights reserved. 7 * 8 * This code is derived from software contributed to The NetBSD Foundation 9 * by IWAMOTO Toshihiro and Ichiro FUKUHARA. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 21 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 24 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30 * POSSIBILITY OF SUCH DAMAGE. 31 */ 32 33 #include <sys/cdefs.h> 34 __KERNEL_RCSID(0, "$NetBSD: sa11x0_ost.c,v 1.24 2008/04/28 20:23:14 martin Exp $"); 35 36 #include <sys/types.h> 37 #include <sys/param.h> 38 #include <sys/systm.h> 39 #include <sys/kernel.h> 40 #include <sys/time.h> 41 #include <sys/timetc.h> 42 #include <sys/device.h> 43 44 #include <machine/bus.h> 45 #include <machine/intr.h> 46 47 #include <arm/cpufunc.h> 48 49 #include <arm/sa11x0/sa11x0_reg.h> 50 #include <arm/sa11x0/sa11x0_var.h> 51 #include <arm/sa11x0/sa11x0_ostreg.h> 52 53 static int saost_match(struct device *, struct cfdata *, void *); 54 static void saost_attach(struct device *, struct device *, void *); 55 56 static void saost_tc_init(void); 57 58 static uint32_t gettick(void); 59 static int clockintr(void *); 60 static int statintr(void *); 61 62 struct saost_softc { 63 struct device sc_dev; 64 65 bus_space_tag_t sc_iot; 66 bus_space_handle_t sc_ioh; 67 68 uint32_t sc_clock_count; 69 uint32_t sc_statclock_count; 70 uint32_t sc_statclock_step; 71 }; 72 73 static struct saost_softc *saost_sc = NULL; 74 75 #if defined(CPU_XSCALE_PXA270) && defined(CPU_XSCALE_PXA250) 76 #error ost needs to dynamically configure the frequency 77 #elif defined(CPU_XSCALE_PXA270) 78 #define TIMER_FREQUENCY 3250000 /* PXA270 uses 3.25MHz */ 79 #else 80 #define TIMER_FREQUENCY 3686400 /* 3.6864MHz */ 81 #endif 82 83 #ifndef STATHZ 84 #define STATHZ 64 85 #endif 86 87 CFATTACH_DECL(saost, sizeof(struct saost_softc), 88 saost_match, saost_attach, NULL, NULL); 89 90 static int 91 saost_match(struct device *parent, struct cfdata *match, void *aux) 92 { 93 94 return 1; 95 } 96 97 static void 98 saost_attach(struct device *parent, struct device *self, void *aux) 99 { 100 struct saost_softc *sc = (struct saost_softc *)self; 101 struct sa11x0_attach_args *sa = aux; 102 103 printf("\n"); 104 105 sc->sc_iot = sa->sa_iot; 106 107 saost_sc = sc; 108 109 if (bus_space_map(sa->sa_iot, sa->sa_addr, sa->sa_size, 0, 110 &sc->sc_ioh)) 111 panic("%s: Cannot map registers", self->dv_xname); 112 113 /* disable all channel and clear interrupt status */ 114 bus_space_write_4(sc->sc_iot, sc->sc_ioh, SAOST_IR, 0); 115 bus_space_write_4(sc->sc_iot, sc->sc_ioh, SAOST_SR, 0xf); 116 117 printf("%s: SA-11x0 OS Timer\n", sc->sc_dev.dv_xname); 118 } 119 120 static int 121 clockintr(void *arg) 122 { 123 struct saost_softc *sc = saost_sc; 124 struct clockframe *frame = arg; 125 uint32_t oscr, nextmatch, oldmatch; 126 int s; 127 128 bus_space_write_4(sc->sc_iot, sc->sc_ioh, SAOST_SR, 1); 129 130 /* schedule next clock intr */ 131 oldmatch = sc->sc_clock_count; 132 nextmatch = oldmatch + TIMER_FREQUENCY / hz; 133 134 bus_space_write_4(sc->sc_iot, sc->sc_ioh, SAOST_MR0, nextmatch); 135 oscr = bus_space_read_4(sc->sc_iot, sc->sc_ioh, SAOST_CR); 136 137 if ((nextmatch > oldmatch && 138 (oscr > nextmatch || oscr < oldmatch)) || 139 (nextmatch < oldmatch && oscr > nextmatch && oscr < oldmatch)) { 140 /* 141 * we couldn't set the matching register in time. 142 * just set it to some value so that next interrupt happens. 143 * XXX is it possible to compensate lost interrupts? 144 */ 145 146 s = splhigh(); 147 oscr = bus_space_read_4(sc->sc_iot, sc->sc_ioh, SAOST_CR); 148 nextmatch = oscr + 10; 149 bus_space_write_4(sc->sc_iot, sc->sc_ioh, SAOST_MR0, nextmatch); 150 splx(s); 151 } 152 153 sc->sc_clock_count = nextmatch; 154 hardclock(frame); 155 156 return 1; 157 } 158 159 static int 160 statintr(void *arg) 161 { 162 struct saost_softc *sc = saost_sc; 163 struct clockframe *frame = arg; 164 uint32_t oscr, nextmatch, oldmatch; 165 int s; 166 167 bus_space_write_4(sc->sc_iot, sc->sc_ioh, SAOST_SR, 2); 168 169 /* schedule next clock intr */ 170 oldmatch = sc->sc_statclock_count; 171 nextmatch = oldmatch + sc->sc_statclock_step; 172 173 bus_space_write_4(sc->sc_iot, sc->sc_ioh, SAOST_MR1, nextmatch); 174 oscr = bus_space_read_4(sc->sc_iot, sc->sc_ioh, SAOST_CR); 175 176 if ((nextmatch > oldmatch && 177 (oscr > nextmatch || oscr < oldmatch)) || 178 (nextmatch < oldmatch && oscr > nextmatch && oscr < oldmatch)) { 179 /* 180 * we couldn't set the matching register in time. 181 * just set it to some value so that next interrupt happens. 182 * XXX is it possible to compensate lost interrupts? 183 */ 184 185 s = splhigh(); 186 oscr = bus_space_read_4(sc->sc_iot, sc->sc_ioh, SAOST_CR); 187 nextmatch = oscr + 10; 188 bus_space_write_4(sc->sc_iot, sc->sc_ioh, SAOST_MR1, nextmatch); 189 splx(s); 190 } 191 192 sc->sc_statclock_count = nextmatch; 193 statclock(frame); 194 195 return 1; 196 } 197 198 void 199 setstatclockrate(int schz) 200 { 201 struct saost_softc *sc = saost_sc; 202 uint32_t count; 203 204 sc->sc_statclock_step = TIMER_FREQUENCY / schz; 205 count = bus_space_read_4(sc->sc_iot, sc->sc_ioh, SAOST_CR); 206 count += sc->sc_statclock_step; 207 sc->sc_statclock_count = count; 208 bus_space_write_4(sc->sc_iot, sc->sc_ioh, SAOST_MR1, count); 209 } 210 211 void 212 cpu_initclocks(void) 213 { 214 struct saost_softc *sc = saost_sc; 215 216 stathz = STATHZ; 217 profhz = stathz; 218 sc->sc_statclock_step = TIMER_FREQUENCY / stathz; 219 220 printf("clock: hz=%d stathz=%d\n", hz, stathz); 221 222 /* Use the channels 0 and 1 for hardclock and statclock, respectively */ 223 sc->sc_clock_count = TIMER_FREQUENCY / hz; 224 sc->sc_statclock_count = TIMER_FREQUENCY / stathz; 225 226 sa11x0_intr_establish(0, 26, 1, IPL_CLOCK, clockintr, 0); 227 sa11x0_intr_establish(0, 27, 1, IPL_CLOCK, statintr, 0); 228 229 bus_space_write_4(sc->sc_iot, sc->sc_ioh, SAOST_SR, 0xf); 230 bus_space_write_4(sc->sc_iot, sc->sc_ioh, SAOST_IR, 3); 231 bus_space_write_4(sc->sc_iot, sc->sc_ioh, SAOST_MR0, 232 sc->sc_clock_count); 233 bus_space_write_4(sc->sc_iot, sc->sc_ioh, SAOST_MR1, 234 sc->sc_statclock_count); 235 236 /* Zero the counter value */ 237 bus_space_write_4(sc->sc_iot, sc->sc_ioh, SAOST_CR, 0); 238 239 saost_tc_init(); 240 } 241 242 static u_int 243 saost_tc_get_timecount(struct timecounter *tc) 244 { 245 return (u_int)gettick(); 246 } 247 248 static void 249 saost_tc_init(void) 250 { 251 static struct timecounter saost_tc = { 252 .tc_get_timecount = saost_tc_get_timecount, 253 .tc_frequency = TIMER_FREQUENCY, 254 .tc_counter_mask = ~0, 255 .tc_name = "saost_count", 256 .tc_quality = 100, 257 }; 258 259 tc_init(&saost_tc); 260 } 261 262 static uint32_t 263 gettick(void) 264 { 265 struct saost_softc *sc = saost_sc; 266 uint32_t counter; 267 u_int saved_ints; 268 269 saved_ints = disable_interrupts(I32_bit); 270 counter = bus_space_read_4(sc->sc_iot, sc->sc_ioh, SAOST_CR); 271 restore_interrupts(saved_ints); 272 273 return counter; 274 } 275 276 void 277 delay(u_int usecs) 278 { 279 uint32_t xtick, otick, delta; 280 int csec, usec; 281 282 csec = usecs / 10000; 283 usec = usecs % 10000; 284 285 usecs = (TIMER_FREQUENCY / 100) * csec 286 + (TIMER_FREQUENCY / 100) * usec / 10000; 287 288 if (saost_sc == NULL) { 289 volatile int k; 290 int j; 291 /* clock isn't initialized yet */ 292 for (; usecs > 0; usecs--) 293 for (j = 100; j > 0; j--, k--) 294 continue; 295 return; 296 } 297 298 otick = gettick(); 299 300 while (1) { 301 xtick = gettick(); 302 delta = xtick - otick; 303 if (delta > usecs) 304 break; 305 usecs -= delta; 306 otick = xtick; 307 } 308 } 309