1 /* $NetBSD: sa11x0_ost.c,v 1.28 2009/08/09 06:12:33 kiyohara 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.28 2009/08/09 06:12:33 kiyohara 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(device_t, cfdata_t, void *); 61 static void saost_attach(device_t, device_t, 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 device_t 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 #include <arm/xscale/pxa2x0cpu.h> 84 static uint32_t freq; 85 #define TIMER_FREQUENCY freq 86 #elif defined(CPU_XSCALE_PXA270) 87 #define TIMER_FREQUENCY 3250000 /* PXA270 uses 3.25MHz */ 88 #else 89 #define TIMER_FREQUENCY 3686400 /* 3.6864MHz */ 90 #endif 91 92 #ifndef STATHZ 93 #define STATHZ 64 94 #endif 95 96 CFATTACH_DECL_NEW(saost, sizeof(struct saost_softc), 97 saost_match, saost_attach, NULL, NULL); 98 99 static int 100 saost_match(device_t parent, cfdata_t match, void *aux) 101 { 102 struct sa11x0_attach_args *sa = aux; 103 104 if (strcmp(sa->sa_name, match->cf_name) != 0) 105 return 0; 106 return 1; 107 } 108 109 static void 110 saost_attach(device_t parent, device_t self, void *aux) 111 { 112 struct saost_softc *sc = device_private(self); 113 struct sa11x0_attach_args *sa = aux; 114 115 aprint_normal("\n"); 116 117 sc->sc_dev = self; 118 sc->sc_iot = sa->sa_iot; 119 120 saost_sc = sc; 121 122 if (bus_space_map(sa->sa_iot, sa->sa_addr, sa->sa_size, 0, 123 &sc->sc_ioh)) 124 panic("%s: Cannot map registers", device_xname(self)); 125 126 /* disable all channel and clear interrupt status */ 127 bus_space_write_4(sc->sc_iot, sc->sc_ioh, SAOST_IR, 0); 128 bus_space_write_4(sc->sc_iot, sc->sc_ioh, SAOST_SR, 0xf); 129 130 aprint_normal_dev(self, "SA-11x0 OS Timer\n"); 131 } 132 133 static int 134 clockintr(void *arg) 135 { 136 struct saost_softc *sc = saost_sc; 137 struct clockframe *frame = arg; 138 uint32_t oscr, nextmatch, oldmatch; 139 int s; 140 141 bus_space_write_4(sc->sc_iot, sc->sc_ioh, SAOST_SR, 1); 142 143 /* schedule next clock intr */ 144 oldmatch = sc->sc_clock_count; 145 nextmatch = oldmatch + TIMER_FREQUENCY / hz; 146 147 bus_space_write_4(sc->sc_iot, sc->sc_ioh, SAOST_MR0, nextmatch); 148 oscr = bus_space_read_4(sc->sc_iot, sc->sc_ioh, SAOST_CR); 149 150 if ((nextmatch > oldmatch && 151 (oscr > nextmatch || oscr < oldmatch)) || 152 (nextmatch < oldmatch && oscr > nextmatch && oscr < oldmatch)) { 153 /* 154 * we couldn't set the matching register in time. 155 * just set it to some value so that next interrupt happens. 156 * XXX is it possible to compensate lost interrupts? 157 */ 158 159 s = splhigh(); 160 oscr = bus_space_read_4(sc->sc_iot, sc->sc_ioh, SAOST_CR); 161 nextmatch = oscr + 10; 162 bus_space_write_4(sc->sc_iot, sc->sc_ioh, SAOST_MR0, nextmatch); 163 splx(s); 164 } 165 166 sc->sc_clock_count = nextmatch; 167 hardclock(frame); 168 169 return 1; 170 } 171 172 static int 173 statintr(void *arg) 174 { 175 struct saost_softc *sc = saost_sc; 176 struct clockframe *frame = arg; 177 uint32_t oscr, nextmatch, oldmatch; 178 int s; 179 180 bus_space_write_4(sc->sc_iot, sc->sc_ioh, SAOST_SR, 2); 181 182 /* schedule next clock intr */ 183 oldmatch = sc->sc_statclock_count; 184 nextmatch = oldmatch + sc->sc_statclock_step; 185 186 bus_space_write_4(sc->sc_iot, sc->sc_ioh, SAOST_MR1, nextmatch); 187 oscr = bus_space_read_4(sc->sc_iot, sc->sc_ioh, SAOST_CR); 188 189 if ((nextmatch > oldmatch && 190 (oscr > nextmatch || oscr < oldmatch)) || 191 (nextmatch < oldmatch && oscr > nextmatch && oscr < oldmatch)) { 192 /* 193 * we couldn't set the matching register in time. 194 * just set it to some value so that next interrupt happens. 195 * XXX is it possible to compensate lost interrupts? 196 */ 197 198 s = splhigh(); 199 oscr = bus_space_read_4(sc->sc_iot, sc->sc_ioh, SAOST_CR); 200 nextmatch = oscr + 10; 201 bus_space_write_4(sc->sc_iot, sc->sc_ioh, SAOST_MR1, nextmatch); 202 splx(s); 203 } 204 205 sc->sc_statclock_count = nextmatch; 206 statclock(frame); 207 208 return 1; 209 } 210 211 void 212 setstatclockrate(int schz) 213 { 214 struct saost_softc *sc = saost_sc; 215 uint32_t count; 216 217 sc->sc_statclock_step = TIMER_FREQUENCY / schz; 218 count = bus_space_read_4(sc->sc_iot, sc->sc_ioh, SAOST_CR); 219 count += sc->sc_statclock_step; 220 sc->sc_statclock_count = count; 221 bus_space_write_4(sc->sc_iot, sc->sc_ioh, SAOST_MR1, count); 222 } 223 224 void 225 cpu_initclocks(void) 226 { 227 struct saost_softc *sc = saost_sc; 228 229 stathz = STATHZ; 230 profhz = stathz; 231 #if defined(CPU_XSCALE_PXA270) && defined(CPU_XSCALE_PXA250) 232 TIMER_FREQUENCY = (CPU_IS_PXA250) ? 3686400 : 3250000; 233 #endif 234 sc->sc_statclock_step = TIMER_FREQUENCY / stathz; 235 236 aprint_normal("clock: hz=%d stathz=%d\n", hz, stathz); 237 238 /* Use the channels 0 and 1 for hardclock and statclock, respectively */ 239 sc->sc_clock_count = TIMER_FREQUENCY / hz; 240 sc->sc_statclock_count = TIMER_FREQUENCY / stathz; 241 242 sa11x0_intr_establish(0, 26, 1, IPL_CLOCK, clockintr, 0); 243 sa11x0_intr_establish(0, 27, 1, IPL_CLOCK, statintr, 0); 244 245 bus_space_write_4(sc->sc_iot, sc->sc_ioh, SAOST_SR, 0xf); 246 bus_space_write_4(sc->sc_iot, sc->sc_ioh, SAOST_IR, 3); 247 bus_space_write_4(sc->sc_iot, sc->sc_ioh, SAOST_MR0, 248 sc->sc_clock_count); 249 bus_space_write_4(sc->sc_iot, sc->sc_ioh, SAOST_MR1, 250 sc->sc_statclock_count); 251 252 /* Zero the counter value */ 253 bus_space_write_4(sc->sc_iot, sc->sc_ioh, SAOST_CR, 0); 254 255 saost_tc_init(); 256 } 257 258 static u_int 259 saost_tc_get_timecount(struct timecounter *tc) 260 { 261 return (u_int)gettick(); 262 } 263 264 static void 265 saost_tc_init(void) 266 { 267 static struct timecounter saost_tc = { 268 .tc_get_timecount = saost_tc_get_timecount, 269 .tc_counter_mask = ~0, 270 .tc_name = "saost_count", 271 #if !(defined(CPU_XSCALE_PXA270) && defined(CPU_XSCALE_PXA250)) 272 .tc_frequency = TIMER_FREQUENCY, 273 #endif 274 .tc_quality = 100, 275 }; 276 277 #if defined(CPU_XSCALE_PXA270) && defined(CPU_XSCALE_PXA250) 278 saost_tc.tc_frequency = TIMER_FREQUENCY, 279 #endif 280 tc_init(&saost_tc); 281 } 282 283 static uint32_t 284 gettick(void) 285 { 286 struct saost_softc *sc = saost_sc; 287 uint32_t counter; 288 u_int saved_ints; 289 290 saved_ints = disable_interrupts(I32_bit); 291 counter = bus_space_read_4(sc->sc_iot, sc->sc_ioh, SAOST_CR); 292 restore_interrupts(saved_ints); 293 294 return counter; 295 } 296 297 void 298 delay(u_int usecs) 299 { 300 uint32_t xtick, otick, delta; 301 int csec, usec; 302 303 csec = usecs / 10000; 304 usec = usecs % 10000; 305 306 usecs = (TIMER_FREQUENCY / 100) * csec 307 + (TIMER_FREQUENCY / 100) * usec / 10000; 308 309 if (saost_sc == NULL) { 310 volatile int k; 311 int j; 312 /* clock isn't initialized yet */ 313 for (; usecs > 0; usecs--) 314 for (j = 100; j > 0; j--, k--) 315 continue; 316 return; 317 } 318 319 otick = gettick(); 320 321 while (1) { 322 xtick = gettick(); 323 delta = xtick - otick; 324 if (delta > usecs) 325 break; 326 usecs -= delta; 327 otick = xtick; 328 } 329 } 330