1 /* $NetBSD: mvsoctmr.c,v 1.3 2012/02/12 16:34:07 matt 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.3 2012/02/12 16:34:07 matt 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 64 static u_int mvsoctmr_get_timecount(struct timecounter *); 65 66 static void mvsoctmr_cntl(struct mvsoctmr_softc *, int, u_int, int, int); 67 68 static struct mvsoctmr_softc *mvsoctmr_sc; 69 static struct timecounter mvsoctmr_timecounter = { 70 mvsoctmr_get_timecount, /* get_timecount */ 71 0, /* no poll_pps */ 72 ~0u, /* counter_mask */ 73 0, /* frequency (set by cpu_initclocks()) */ 74 "mvsoctmr", /* name */ 75 100, /* quality */ 76 NULL, /* prev */ 77 NULL, /* next */ 78 }; 79 80 CFATTACH_DECL_NEW(mvsoctmr, sizeof(struct mvsoctmr_softc), 81 mvsoctmr_match, mvsoctmr_attach, NULL, NULL); 82 83 84 /* ARGSUSED */ 85 static int 86 mvsoctmr_match(device_t parent, struct cfdata *match, void *aux) 87 { 88 struct marvell_attach_args *mva = aux; 89 90 if (strcmp(mva->mva_name, match->cf_name) != 0) 91 return 0; 92 if (mva->mva_offset == MVA_OFFSET_DEFAULT) 93 return 0; 94 95 mva->mva_size = MVSOCTMR_SIZE; 96 return 1; 97 } 98 99 /* ARGSUSED */ 100 static void 101 mvsoctmr_attach(device_t parent, device_t self, void *aux) 102 { 103 struct mvsoctmr_softc *sc = device_private(self); 104 struct marvell_attach_args *mva = aux; 105 106 aprint_naive("\n"); 107 aprint_normal(": Marvell SoC Timer\n"); 108 109 if (mvsoctmr_sc == NULL) 110 mvsoctmr_sc = sc; 111 112 sc->sc_dev = self; 113 sc->sc_iot = mva->mva_iot; 114 if (bus_space_subregion(mva->mva_iot, mva->mva_ioh, 115 mva->mva_offset, mva->mva_size, &sc->sc_ioh)) 116 panic("%s: Cannot map registers", device_xname(self)); 117 118 mvsoctmr_timecounter.tc_name = device_xname(self); 119 mvsoctmr_cntl(sc, MVSOCTMR_TIMER1, 0xffffffff, 1, 1); 120 } 121 122 /* 123 * clockhandler: 124 * 125 * Handle the hardclock interrupt. 126 */ 127 static int 128 clockhandler(void *arg) 129 { 130 struct clockframe *frame = arg; 131 132 hardclock(frame); 133 134 return 1; 135 } 136 137 /* 138 * setstatclockrate: 139 * 140 * Set the rate of the statistics clock. 141 */ 142 /* ARGSUSED */ 143 void 144 setstatclockrate(int newhz) 145 { 146 } 147 148 /* 149 * cpu_initclocks: 150 * 151 * Initialize the clock and get them going. 152 */ 153 void 154 cpu_initclocks(void) 155 { 156 struct mvsoctmr_softc *sc; 157 void *clock_ih; 158 const int en = 1, autoen = 1; 159 uint32_t timer0_tval; 160 161 sc = mvsoctmr_sc; 162 if (sc == NULL) 163 panic("cpu_initclocks: mvsoctmr not found"); 164 165 mvsoctmr_timecounter.tc_priv = sc; 166 mvsoctmr_timecounter.tc_frequency = mvTclk; 167 168 timer0_tval = (mvTclk * 2) / (u_long) hz; 169 timer0_tval = (timer0_tval / 2) + (timer0_tval & 1); 170 171 mvsoctmr_cntl(sc, MVSOCTMR_TIMER0, timer0_tval, en, autoen); 172 mvsoctmr_cntl(sc, MVSOCTMR_TIMER1, 0xffffffff, en, autoen); 173 174 clock_ih = mvsoc_bridge_intr_establish(MVSOC_MLMB_MLMBI_CPUTIMER0INTREQ, 175 IPL_CLOCK, clockhandler, NULL); 176 if (clock_ih == NULL) 177 panic("cpu_initclocks: unable to register timer interrupt"); 178 179 tc_init(&mvsoctmr_timecounter); 180 } 181 182 void 183 delay(unsigned int n) 184 { 185 struct mvsoctmr_softc *sc; 186 unsigned int cur_tick, initial_tick; 187 int remaining; 188 189 sc = mvsoctmr_sc; 190 #ifdef DEBUG 191 if (sc == NULL) { 192 printf("%s: called before start mvsoctmr\n", __func__); 193 return; 194 } 195 #endif 196 197 /* 198 * Read the counter first, so that the rest of the setup overhead is 199 * counted. 200 */ 201 initial_tick = bus_space_read_4(sc->sc_iot, sc->sc_ioh, 202 MVSOCTMR_TIMER(MVSOCTMR_TIMER1)); 203 204 if (n <= UINT_MAX / mvTclk) { 205 /* 206 * For unsigned arithmetic, division can be replaced with 207 * multiplication with the inverse and a shift. 208 */ 209 remaining = n * mvTclk / 1000000; 210 } else { 211 /* 212 * This is a very long delay. 213 * Being slow here doesn't matter. 214 */ 215 remaining = (unsigned long long) n * mvTclk / 1000000; 216 } 217 218 while (remaining > 0) { 219 cur_tick = bus_space_read_4(sc->sc_iot, sc->sc_ioh, 220 MVSOCTMR_TIMER(MVSOCTMR_TIMER1)); 221 if (cur_tick > initial_tick) 222 remaining -= 0xffffffff - cur_tick + initial_tick; 223 else 224 remaining -= (initial_tick - cur_tick); 225 initial_tick = cur_tick; 226 } 227 } 228 229 static u_int 230 mvsoctmr_get_timecount(struct timecounter *tc) 231 { 232 struct mvsoctmr_softc *sc = tc->tc_priv; 233 234 return 0xffffffff - bus_space_read_4(sc->sc_iot, sc->sc_ioh, 235 MVSOCTMR_TIMER(MVSOCTMR_TIMER1)); 236 } 237 238 static void 239 mvsoctmr_cntl(struct mvsoctmr_softc *sc, int num, u_int ticks, int en, 240 int autoen) 241 { 242 uint32_t ctrl; 243 244 bus_space_write_4(sc->sc_iot, sc->sc_ioh, MVSOCTMR_RELOAD(num), 245 ticks); 246 247 bus_space_write_4(sc->sc_iot, sc->sc_ioh, MVSOCTMR_TIMER(num), ticks); 248 249 ctrl = bus_space_read_4(sc->sc_iot, sc->sc_ioh, MVSOCTMR_CTCR); 250 if (en) 251 ctrl |= MVSOCTMR_CTCR_CPUTIMEREN(num); 252 else 253 ctrl &= ~MVSOCTMR_CTCR_CPUTIMEREN(num); 254 if (autoen) 255 ctrl |= MVSOCTMR_CTCR_CPUTIMERAUTO(num); 256 else 257 ctrl &= ~MVSOCTMR_CTCR_CPUTIMERAUTO(num); 258 bus_space_write_4(sc->sc_iot, sc->sc_ioh, MVSOCTMR_CTCR, ctrl); 259 } 260