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