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