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