1/* $NetBSD: ixp12x0_clk.c,v 1.19 2021/07/31 14:36:33 andvar 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.19 2021/07/31 14:36:33 andvar 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 63static int ixpclk_match(device_t, cfdata_t, void *); 64static void ixpclk_attach(device_t, device_t, void *); 65 66static u_int ixpclk_get_timecount(struct timecounter *); 67 68int gettick(void); 69void rtcinit(void); 70 71/* callback functions for intr_functions */ 72static int ixpclk_intr(void* arg); 73 74struct 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) 92static 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 123static struct ixpclk_softc *ixpclk_sc = NULL; 124 125static struct timecounter ixpclk_timecounter = { 126 .tc_get_timecount = ixpclk_get_timecount, 127 .tc_counter_mask = 0xffffffff, 128 .tc_name = "ixpclk", 129 .tc_quality = 100, 130}; 131 132static volatile uint32_t ixpclk_base; 133 134#define TIMER_FREQUENCY 3686400 /* 3.6864MHz */ 135#define TICKS_PER_MICROSECOND (TIMER_FREQUENCY/1000000) 136 137CFATTACH_DECL_NEW(ixpclk, sizeof(struct ixpclk_softc), 138 ixpclk_match, ixpclk_attach, NULL, NULL); 139 140#define GET_TIMER_VALUE(sc) (bus_space_read_4((sc)->sc_iot, \ 141 (sc)->sc_ioh, \ 142 IXPCLK_VALUE) \ 143 & IXPCL_CTV) 144 145static int 146ixpclk_match(device_t parent, cfdata_t match, void *aux) 147{ 148 149 return 2; 150} 151 152static void 153ixpclk_attach(device_t parent, device_t self, void *aux) 154{ 155 struct ixpclk_softc *sc; 156 struct ixpsip_attach_args *sa; 157 uint32_t ccf; 158 bool first_run = ixpclk_sc == NULL; 159 160 printf("\n"); 161 162 sc = device_private(self); 163 sa = aux; 164 sc->sc_iot = sa->sa_iot; 165 sc->sc_baseaddr = sa->sa_addr; 166 167 /* using first timer for system ticks */ 168 if (ixpclk_sc == NULL) 169 ixpclk_sc = sc; 170 171 if (bus_space_map(sa->sa_iot, sa->sa_addr, sa->sa_size, 0, 172 &sc->sc_ioh)) 173 panic("%s: Cannot map registers", device_xname(self)); 174 if (bus_space_map(sa->sa_iot, sa->sa_addr + IXPCLK_PLL_CFG_OFFSET, 175 IXPCLK_PLL_CFG_SIZE, 0, &sc->sc_pll_ioh)) 176 panic("%s: Cannot map registers", device_xname(self)); 177 178 /* disable all channel and clear interrupt status */ 179 bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXPCLK_CONTROL, 180 IXPCL_DISABLE | IXPCL_PERIODIC | IXPCL_STP_CORE); 181 bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXPCLK_CLEAR, 0); 182 183 184 ccf = bus_space_read_4(sc->sc_iot, sc->sc_pll_ioh, 0) 185 & IXP12X0_PLL_CFG_CCF; 186 sc->sc_coreclock_freq = ccf_to_coreclock[ccf]; 187 188 sc->sc_clock_count = sc->sc_coreclock_freq / hz; 189 sc->sc_count_per_usec = sc->sc_coreclock_freq / 1000000; 190 191 bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXPCLK_CLEAR, IXPT_CLEAR); 192 bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXPCLK_LOAD, 193 sc->sc_clock_count); 194 bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXPCLK_CONTROL, 195 IXPCL_ENABLE | IXPCL_PERIODIC | IXPCL_STP_CORE); 196 197 if (first_run) { 198 ixpclk_timecounter.tc_frequency = sc->sc_coreclock_freq; 199 tc_init(&ixpclk_timecounter); 200 } 201 202 printf("%s: IXP12x0 Interval Timer (core clock %d.%03dMHz)\n", 203 device_xname(self), 204 sc->sc_coreclock_freq / 1000000, 205 (sc->sc_coreclock_freq % 1000000) / 1000); 206} 207 208/* 209 * ixpclk_intr: 210 * 211 * Handle the hardclock interrupt. 212 */ 213static int 214ixpclk_intr(void *arg) 215{ 216 217 bus_space_write_4(ixpclk_sc->sc_iot, ixpclk_sc->sc_ioh, 218 IXPCLK_CLEAR, 1); 219 220 atomic_add_32(&ixpclk_base, ixpclk_sc->sc_coreclock_freq); 221 222 hardclock((struct clockframe*) arg); 223 return (1); 224} 225 226/* 227 * setstatclockrate: 228 * 229 * Set the rate of the statistics clock. 230 * 231 * We assume that hz is either stathz or profhz, and that neither 232 * will change after being set by cpu_initclocks(). We could 233 * recalculate the intervals here, but that would be a pain. 234 */ 235void 236setstatclockrate(int newhz) 237{ 238 239 /* use hardclock */ 240 241 /* XXX should I use TIMER2? */ 242} 243 244/* 245 * cpu_initclocks: 246 * 247 * Initialize the clock and get them going. 248 */ 249void 250cpu_initclocks(void) 251{ 252 struct ixpclk_softc* sc; 253 254 sc = ixpclk_sc; 255 stathz = profhz = 0; 256 257 printf("clock: hz = %d stathz = %d\n", hz, stathz); 258 259 bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXPCLK_CONTROL, 260 IXPCL_DISABLE); 261 bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXPCLK_CLEAR, IXPT_CLEAR); 262 263 ixp12x0_intr_establish(IXPPCI_INTR_T1, IPL_CLOCK, ixpclk_intr, NULL); 264 265 bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXPCLK_LOAD, 266 sc->sc_clock_count); 267 bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXPCLK_CONTROL, 268 IXPCL_ENABLE | IXPCL_PERIODIC 269 | IXPCL_STP_CORE); 270} 271 272int 273gettick(void) 274{ 275 int counter; 276 u_int savedints; 277 278 savedints = disable_interrupts(I32_bit); 279 counter = GET_TIMER_VALUE(ixpclk_sc); 280 restore_interrupts(savedints); 281 return counter; 282} 283 284static u_int 285ixpclk_get_timecount(struct timecounter *tc) 286{ 287 u_int savedints, base, counter; 288 289 savedints = disable_interrupts(I32_bit); 290 do { 291 base = ixpclk_base; 292 counter = GET_TIMER_VALUE(ixpclk_sc); 293 } while (base != ixpclk_base); 294 restore_interrupts(savedints); 295 296 return base - counter; 297} 298 299/* 300 * delay: 301 * 302 * Delay for at least N microseconds. 303 */ 304void 305delay(unsigned int usecs) 306{ 307 uint32_t count; 308 uint32_t ticks; 309 uint32_t otick; 310 uint32_t delta; 311 int j; 312 int csec; 313 int usec; 314 315 if (ixpclk_sc == NULL) { 316#ifdef DEBUG 317 printf("delay: called before start ixpclk\n"); 318#endif 319 320 csec = usecs / 10000; 321 usec = usecs % 10000; 322 323 usecs = (TIMER_FREQUENCY / 100) * csec 324 + (TIMER_FREQUENCY / 100) * usec / 10000; 325 /* clock isn't initialized yet */ 326 for(; usecs > 0; usecs--) 327 for(j = 100; j > 0; j--) 328 ; 329 return; 330 } 331 332 count = ixpclk_sc->sc_count_per_usec * usecs; 333 334 otick = gettick(); 335 336 for (;;) { 337 for(j = 100; j > 0; j--) 338 ; 339 340 ticks = gettick(); 341 delta = otick < ticks 342 ? ixpclk_sc->sc_clock_count + otick - ticks 343 : otick - ticks; 344 345 if (delta > count) 346 break; 347 348 count -= delta; 349 otick = ticks; 350 } 351} 352