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