1 /* $NetBSD: ixp12x0_clk.c,v 1.14 2008/05/10 15:31:04 martin 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: ixp12x0_clk.c,v 1.14 2008/05/10 15:31:04 martin Exp $"); 42 43 #include <sys/types.h> 44 #include <sys/param.h> 45 #include <sys/atomic.h> 46 #include <sys/systm.h> 47 #include <sys/kernel.h> 48 #include <sys/time.h> 49 #include <sys/timetc.h> 50 #include <sys/device.h> 51 52 #include <machine/bus.h> 53 #include <machine/intr.h> 54 55 #include <arm/cpufunc.h> 56 57 #include <arm/ixp12x0/ixpsipvar.h> 58 59 #include <arm/ixp12x0/ixp12x0_pcireg.h> 60 #include <arm/ixp12x0/ixp12x0_clkreg.h> 61 #include <arm/ixp12x0/ixp12x0var.h> 62 63 static int ixpclk_match(struct device *, struct cfdata *, void *); 64 static void ixpclk_attach(struct device *, struct device *, void *); 65 66 static u_int ixpclk_get_timecount(struct timecounter *); 67 68 int gettick(void); 69 void rtcinit(void); 70 71 /* callback functions for intr_functions */ 72 static int ixpclk_intr(void* arg); 73 74 struct ixpclk_softc { 75 struct device sc_dev; 76 bus_addr_t sc_baseaddr; 77 bus_space_tag_t sc_iot; 78 bus_space_handle_t sc_ioh; 79 bus_space_handle_t sc_pll_ioh; 80 81 u_int32_t sc_clock_count; 82 u_int32_t sc_count_per_usec; 83 u_int32_t sc_coreclock_freq; 84 }; 85 86 #define XTAL_FREQ 3686400 /* 3.6864MHz */ 87 #define XTAL_FREQ3686400 88 #undef XTAL_FREQ3787800 89 #undef XTAL_FREQ3579500 90 #define MAX_CCF 22 91 92 #if defined(XTAL_FREQ3686400) 93 static u_int32_t ccf_to_coreclock[MAX_CCF + 1] = { 94 29491000, 95 36865000, 96 44237000, 97 51610000, 98 58982000, 99 66355000, 100 73728000, 101 81101000, 102 88474000, 103 95846000, 104 103219000, 105 110592000, 106 132710000, 107 147456000, 108 154829000, 109 162202000, 110 165890000, 111 176947000, 112 191693000, 113 199066000, 114 206438000, 115 221184000, 116 232243000, 117 }; 118 #elif defined(XTAL_FREQ3787800) 119 #elif defined(XTAL_FREQ3579500) 120 #else 121 #error 122 #endif 123 124 static struct ixpclk_softc *ixpclk_sc = NULL; 125 126 static struct timecounter ixpclk_timecounter = { 127 ixpclk_get_timecount, /* get_timecount */ 128 0, /* no poll_pps */ 129 0xffffffff, /* counter_mask */ 130 0, /* frequency */ 131 "ixpclk", /* name */ 132 100, /* quality */ 133 NULL, /* prev */ 134 NULL, /* next */ 135 }; 136 137 static volatile uint32_t ixpclk_base; 138 139 #define TIMER_FREQUENCY 3686400 /* 3.6864MHz */ 140 #define TICKS_PER_MICROSECOND (TIMER_FREQUENCY/1000000) 141 142 CFATTACH_DECL(ixpclk, sizeof(struct ixpclk_softc), 143 ixpclk_match, ixpclk_attach, NULL, NULL); 144 145 #define GET_TIMER_VALUE(sc) (bus_space_read_4((sc)->sc_iot, \ 146 (sc)->sc_ioh, \ 147 IXPCLK_VALUE) \ 148 & IXPCL_CTV) 149 150 static int 151 ixpclk_match(struct device *parent, struct cfdata *match, void *aux) 152 { 153 154 return 2; 155 } 156 157 static void 158 ixpclk_attach(struct device *parent, struct device *self, void *aux) 159 { 160 struct ixpclk_softc *sc; 161 struct ixpsip_attach_args *sa; 162 u_int32_t ccf; 163 bool first_run = ixpclk_sc == NULL; 164 165 printf("\n"); 166 167 sc = (struct ixpclk_softc*) self; 168 sa = aux; 169 sc->sc_iot = sa->sa_iot; 170 sc->sc_baseaddr = sa->sa_addr; 171 172 /* using first timer for system ticks */ 173 if (ixpclk_sc == NULL) 174 ixpclk_sc = sc; 175 176 if (bus_space_map(sa->sa_iot, sa->sa_addr, sa->sa_size, 0, 177 &sc->sc_ioh)) 178 panic("%s: Cannot map registers", self->dv_xname); 179 if (bus_space_map(sa->sa_iot, sa->sa_addr + IXPCLK_PLL_CFG_OFFSET, 180 IXPCLK_PLL_CFG_SIZE, 0, &sc->sc_pll_ioh)) 181 panic("%s: Cannot map registers", self->dv_xname); 182 183 /* disable all channel and clear interrupt status */ 184 bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXPCLK_CONTROL, 185 IXPCL_DISABLE | IXPCL_PERIODIC | IXPCL_STP_CORE); 186 bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXPCLK_CLEAR, 0); 187 188 189 ccf = bus_space_read_4(sc->sc_iot, sc->sc_pll_ioh, 0) 190 & IXP12X0_PLL_CFG_CCF; 191 sc->sc_coreclock_freq = ccf_to_coreclock[ccf]; 192 193 sc->sc_clock_count = sc->sc_coreclock_freq / hz; 194 sc->sc_count_per_usec = sc->sc_coreclock_freq / 1000000; 195 196 bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXPCLK_CLEAR, IXPT_CLEAR); 197 bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXPCLK_LOAD, 198 sc->sc_clock_count); 199 bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXPCLK_CONTROL, 200 IXPCL_ENABLE | IXPCL_PERIODIC | IXPCL_STP_CORE); 201 202 if (first_run) { 203 ixpclk_timecounter.tc_frequency = sc->sc_coreclock_freq; 204 tc_init(&ixpclk_timecounter); 205 } 206 207 printf("%s: IXP12x0 Interval Timer (core clock %d.%03dMHz)\n", 208 sc->sc_dev.dv_xname, 209 sc->sc_coreclock_freq / 1000000, 210 (sc->sc_coreclock_freq % 1000000) / 1000); 211 } 212 213 /* 214 * ixpclk_intr: 215 * 216 * Handle the hardclock interrupt. 217 */ 218 static int 219 ixpclk_intr(void *arg) 220 { 221 222 bus_space_write_4(ixpclk_sc->sc_iot, ixpclk_sc->sc_ioh, 223 IXPCLK_CLEAR, 1); 224 225 atomic_add_32(&ixpclk_base, ixpclk_sc->sc_coreclock_freq); 226 227 hardclock((struct clockframe*) arg); 228 return (1); 229 } 230 231 /* 232 * setstatclockrate: 233 * 234 * Set the rate of the statistics clock. 235 * 236 * We assume that hz is either stathz or profhz, and that neither 237 * will change after being set by cpu_initclocks(). We could 238 * recalculate the intervals here, but that would be a pain. 239 */ 240 void 241 setstatclockrate(int newhz) 242 { 243 244 /* use hardclock */ 245 246 /* XXX should I use TIMER2? */ 247 } 248 249 /* 250 * cpu_initclocks: 251 * 252 * Initialize the clock and get them going. 253 */ 254 void 255 cpu_initclocks(void) 256 { 257 struct ixpclk_softc* sc; 258 259 sc = ixpclk_sc; 260 stathz = profhz = 0; 261 262 printf("clock: hz = %d stathz = %d\n", hz, stathz); 263 264 bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXPCLK_CONTROL, 265 IXPCL_DISABLE); 266 bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXPCLK_CLEAR, IXPT_CLEAR); 267 268 ixp12x0_intr_establish(IXPPCI_INTR_T1, IPL_CLOCK, ixpclk_intr, NULL); 269 270 bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXPCLK_LOAD, 271 sc->sc_clock_count); 272 bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXPCLK_CONTROL, 273 IXPCL_ENABLE | IXPCL_PERIODIC 274 | IXPCL_STP_CORE); 275 } 276 277 int 278 gettick(void) 279 { 280 int counter; 281 u_int savedints; 282 283 savedints = disable_interrupts(I32_bit); 284 counter = GET_TIMER_VALUE(ixpclk_sc); 285 restore_interrupts(savedints); 286 return counter; 287 } 288 289 static u_int 290 ixpclk_get_timecount(struct timecounter *tc) 291 { 292 u_int savedints, base, counter; 293 294 savedints = disable_interrupts(I32_bit); 295 do { 296 base = ixpclk_base; 297 counter = GET_TIMER_VALUE(ixpclk_sc); 298 } while (base != ixpclk_base); 299 restore_interrupts(savedints); 300 301 return base - counter; 302 } 303 304 /* 305 * delay: 306 * 307 * Delay for at least N microseconds. 308 */ 309 void 310 delay(unsigned int usecs) 311 { 312 u_int32_t count; 313 u_int32_t ticks; 314 u_int32_t otick; 315 u_int32_t delta; 316 int j; 317 int csec; 318 int usec; 319 320 if (ixpclk_sc == NULL) { 321 #ifdef DEBUG 322 printf("delay: called befor start ixpclk\n"); 323 #endif 324 325 csec = usecs / 10000; 326 usec = usecs % 10000; 327 328 usecs = (TIMER_FREQUENCY / 100) * csec 329 + (TIMER_FREQUENCY / 100) * usec / 10000; 330 /* clock isn't initialized yet */ 331 for(; usecs > 0; usecs--) 332 for(j = 100; j > 0; j--) 333 ; 334 return; 335 } 336 337 count = ixpclk_sc->sc_count_per_usec * usecs; 338 339 otick = gettick(); 340 341 for (;;) { 342 for(j = 100; j > 0; j--) 343 ; 344 345 ticks = gettick(); 346 delta = otick < ticks 347 ? ixpclk_sc->sc_clock_count + otick - ticks 348 : otick - ticks; 349 350 if (delta > count) 351 break; 352 353 count -= delta; 354 otick = ticks; 355 } 356 } 357