1 /* $NetBSD: mvsoctmr.c,v 1.1 2010/10/03 05:49:24 kiyohara Exp $ */ 2 /* 3 * Copyright (c) 2007, 2008 KIYOHARA Takashi 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 19 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 23 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 24 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 25 * POSSIBILITY OF SUCH DAMAGE. 26 */ 27 #include <sys/cdefs.h> 28 __KERNEL_RCSID(0, "$NetBSD: mvsoctmr.c,v 1.1 2010/10/03 05:49:24 kiyohara Exp $"); 29 30 #include <sys/param.h> 31 #include <sys/atomic.h> 32 #include <sys/bus.h> 33 #include <sys/device.h> 34 #include <sys/errno.h> 35 #include <sys/kernel.h> 36 #include <sys/time.h> 37 #include <sys/timetc.h> 38 #include <sys/systm.h> 39 40 #include <machine/intr.h> 41 42 #include <arm/cpufunc.h> 43 44 #include <arm/marvell/mvsocreg.h> 45 #include <arm/marvell/mvsocvar.h> 46 #include <arm/marvell/mvsoctmrreg.h> 47 48 #include <dev/marvell/marvellvar.h> 49 50 51 struct mvsoctmr_softc { 52 device_t sc_dev; 53 54 bus_space_tag_t sc_iot; 55 bus_space_handle_t sc_ioh; 56 }; 57 58 59 static int mvsoctmr_match(device_t, struct cfdata *, void *); 60 static void mvsoctmr_attach(device_t, device_t, void *); 61 62 static int clockhandler(void *); 63 static int statclockhandler(void *); 64 65 static u_int mvsoctmr_get_timecount(struct timecounter *); 66 67 static void mvsoctmr_cntl(struct mvsoctmr_softc *, int, u_int, int, int); 68 69 #ifndef STATHZ 70 #define STATHZ 64 71 #endif 72 73 static struct mvsoctmr_softc *mvsoctmr_sc; 74 static uint32_t clock_ticks, statclock_ticks; 75 static struct timecounter mvsoctmr_timecounter = { 76 mvsoctmr_get_timecount, /* get_timecount */ 77 0, /* no poll_pps */ 78 ~0u, /* counter_mask */ 79 0, /* frequency (set by cpu_initclocks()) */ 80 "mvsoctmr", /* name */ 81 100, /* quality */ 82 NULL, /* prev */ 83 NULL, /* next */ 84 }; 85 static volatile uint32_t mvsoctmr_base; 86 87 CFATTACH_DECL_NEW(mvsoctmr, sizeof(struct mvsoctmr_softc), 88 mvsoctmr_match, mvsoctmr_attach, NULL, NULL); 89 90 91 /* ARGSUSED */ 92 static int 93 mvsoctmr_match(device_t parent, struct cfdata *match, void *aux) 94 { 95 struct marvell_attach_args *mva = aux; 96 97 if (strcmp(mva->mva_name, match->cf_name) != 0) 98 return 0; 99 if (mva->mva_offset == MVA_OFFSET_DEFAULT) 100 return 0; 101 102 mva->mva_size = MVSOCTMR_SIZE; 103 return 1; 104 } 105 106 /* ARGSUSED */ 107 static void 108 mvsoctmr_attach(device_t parent, device_t self, void *aux) 109 { 110 struct mvsoctmr_softc *sc = device_private(self); 111 struct marvell_attach_args *mva = aux; 112 113 aprint_naive("\n"); 114 aprint_normal(": Marvell SoC Timer\n"); 115 116 if (mvsoctmr_sc == NULL) 117 mvsoctmr_sc = sc; 118 119 sc->sc_dev = self; 120 sc->sc_iot = mva->mva_iot; 121 if (bus_space_subregion(mva->mva_iot, mva->mva_ioh, 122 mva->mva_offset, mva->mva_size, &sc->sc_ioh)) 123 panic("%s: Cannot map registers", device_xname(self)); 124 } 125 126 /* 127 * clockhandler: 128 * 129 * Handle the hardclock interrupt. 130 */ 131 static int 132 clockhandler(void *arg) 133 { 134 struct clockframe *frame = arg; 135 136 atomic_add_32(&mvsoctmr_base, clock_ticks); 137 138 hardclock(frame); 139 140 return 1; 141 } 142 143 /* 144 * statclockhandler: 145 * 146 * Handle the statclock interrupt. 147 */ 148 static int 149 statclockhandler(void *arg) 150 { 151 struct clockframe *frame = arg; 152 153 statclock(frame); 154 155 return 1; 156 } 157 158 159 /* 160 * setstatclockrate: 161 * 162 * Set the rate of the statistics clock. 163 * 164 * We assume that hz is either stathz or profhz, and that neither 165 * will change after being set by cpu_initclocks(). We could 166 * recalculate the intervals here, but that would be a pain. 167 */ 168 /* ARGSUSED */ 169 void 170 setstatclockrate(int newhz) 171 { 172 struct mvsoctmr_softc *sc = mvsoctmr_sc; 173 const int en = 1, autoen = 1; 174 175 statclock_ticks = mvTclk / newhz; 176 177 mvsoctmr_cntl(sc, MVSOCTMR_TIMER1, statclock_ticks, en, autoen); 178 } 179 180 /* 181 * cpu_initclocks: 182 * 183 * Initialize the clock and get them going. 184 */ 185 void 186 cpu_initclocks() 187 { 188 struct mvsoctmr_softc *sc; 189 void *clock_ih; 190 const int en = 1, autoen = 1; 191 192 sc = mvsoctmr_sc; 193 if (sc == NULL) 194 panic("cpu_initclocks: mvsoctmr not found"); 195 196 stathz = profhz = STATHZ; 197 mvsoctmr_timecounter.tc_frequency = mvTclk; 198 clock_ticks = mvTclk / hz; 199 200 mvsoctmr_cntl(sc, MVSOCTMR_TIMER0, clock_ticks, en, autoen); 201 202 clock_ih = mvsoc_bridge_intr_establish(MVSOC_MLMB_MLMBI_CPUTIMER0INTREQ, 203 IPL_CLOCK, clockhandler, NULL); 204 if (clock_ih == NULL) 205 panic("cpu_initclocks: unable to register timer interrupt"); 206 207 if (stathz) { 208 setstatclockrate(stathz); 209 clock_ih = mvsoc_bridge_intr_establish( 210 MVSOC_MLMB_MLMBI_CPUTIMER1INTREQ, IPL_HIGH, 211 statclockhandler, NULL); 212 if (clock_ih == NULL) 213 panic("cpu_initclocks:" 214 " unable to register statclock timer interrupt"); 215 } 216 217 tc_init(&mvsoctmr_timecounter); 218 } 219 220 void 221 delay(unsigned int n) 222 { 223 struct mvsoctmr_softc *sc; 224 unsigned int cur_tick, initial_tick; 225 int remaining; 226 227 sc = mvsoctmr_sc; 228 #ifdef DEBUG 229 if (sc == NULL) { 230 printf("%s: called before start mvsoctmr\n", __func__); 231 return; 232 } 233 #endif 234 235 /* 236 * Read the counter first, so that the rest of the setup overhead is 237 * counted. 238 */ 239 initial_tick = bus_space_read_4(sc->sc_iot, sc->sc_ioh, 240 MVSOCTMR_TIMER(MVSOCTMR_TIMER0)); 241 242 if (n <= UINT_MAX / mvTclk) { 243 /* 244 * For unsigned arithmetic, division can be replaced with 245 * multiplication with the inverse and a shift. 246 */ 247 remaining = n * mvTclk / 1000000; 248 } else { 249 /* 250 * This is a very long delay. 251 * Being slow here doesn't matter. 252 */ 253 remaining = (unsigned long long) n * mvTclk / 1000000; 254 } 255 256 while (remaining > 0) { 257 cur_tick = bus_space_read_4(sc->sc_iot, sc->sc_ioh, 258 MVSOCTMR_TIMER(MVSOCTMR_TIMER0)); 259 if (cur_tick > initial_tick) 260 remaining -= clock_ticks - cur_tick + initial_tick; 261 else 262 remaining -= (initial_tick - cur_tick); 263 initial_tick = cur_tick; 264 } 265 } 266 267 static u_int 268 mvsoctmr_get_timecount(struct timecounter *tc) 269 { 270 struct mvsoctmr_softc *sc = mvsoctmr_sc; 271 uint32_t counter, base; 272 u_int intrstat; 273 274 intrstat = disable_interrupts(I32_bit); 275 base = mvsoctmr_base; 276 counter = bus_space_read_4(sc->sc_iot, sc->sc_ioh, 277 MVSOCTMR_TIMER(MVSOCTMR_TIMER0)); 278 restore_interrupts(intrstat); 279 280 return base - counter; 281 } 282 283 284 static void 285 mvsoctmr_cntl(struct mvsoctmr_softc *sc, int num, u_int ticks, int en, 286 int autoen) 287 { 288 uint32_t ctrl; 289 290 bus_space_write_4(sc->sc_iot, sc->sc_ioh, MVSOCTMR_RELOAD(num), 291 ticks); 292 293 bus_space_write_4(sc->sc_iot, sc->sc_ioh, MVSOCTMR_TIMER(num), ticks); 294 295 ctrl = bus_space_read_4(sc->sc_iot, sc->sc_ioh, MVSOCTMR_CTCR); 296 if (en) 297 ctrl |= MVSOCTMR_CTCR_CPUTIMEREN(num); 298 else 299 ctrl &= ~MVSOCTMR_CTCR_CPUTIMEREN(num); 300 if (autoen) 301 ctrl |= MVSOCTMR_CTCR_CPUTIMERAUTO(num); 302 else 303 ctrl &= ~MVSOCTMR_CTCR_CPUTIMERAUTO(num); 304 bus_space_write_4(sc->sc_iot, sc->sc_ioh, MVSOCTMR_CTCR, ctrl); 305 } 306