1/*- 2 * Copyright (c) 2005, 2009-2011 Marcel Moolenaar 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 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 WARRANTIES 17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27#include <sys/cdefs.h> 28__FBSDID("$FreeBSD$"); 29 30#include <sys/param.h> 31#include <sys/kernel.h> 32#include <sys/bus.h> 33#include <sys/interrupt.h> 34#include <sys/priority.h> 35#include <sys/proc.h> 36#include <sys/queue.h> 37#include <sys/sysctl.h> 38#include <sys/systm.h> 39#include <sys/timeet.h> 40#include <sys/timetc.h> 41#include <sys/pcpu.h> 42 43#include <machine/cpu.h> 44#include <machine/efi.h> 45#include <machine/intr.h> 46#include <machine/intrcnt.h> 47#include <machine/md_var.h> 48#include <machine/smp.h> 49 50#define CLOCK_ET_OFF 0 51#define CLOCK_ET_PERIODIC 1 52#define CLOCK_ET_ONESHOT 2 53 54static struct eventtimer ia64_clock_et; 55static u_int ia64_clock_xiv; 56 57#ifndef SMP 58static timecounter_get_t ia64_get_timecount; 59 60static struct timecounter ia64_timecounter = { 61 ia64_get_timecount, /* get_timecount */ 62 0, /* no poll_pps */ 63 ~0u, /* counter_mask */ 64 0, /* frequency */ 65 "ITC" /* name */ 66}; 67 68static u_int 69ia64_get_timecount(struct timecounter* tc) 70{ 71 return ia64_get_itc(); 72} 73#endif 74 75static u_int 76ia64_ih_clock(struct thread *td, u_int xiv, struct trapframe *tf) 77{ 78 struct eventtimer *et; 79 uint64_t itc, load; 80 uint32_t mode; 81 82 PCPU_INC(md.stats.pcs_nclks); 83 intrcnt[INTRCNT_CLOCK]++; 84 85 itc = ia64_get_itc(); 86 PCPU_SET(md.clock, itc); 87 88 mode = PCPU_GET(md.clock_mode); 89 if (mode == CLOCK_ET_PERIODIC) { 90 load = PCPU_GET(md.clock_load); 91 ia64_set_itm(itc + load); 92 } else 93 ia64_set_itv((1 << 16) | xiv); 94 95 ia64_set_eoi(0); 96 ia64_srlz_d(); 97 98 et = &ia64_clock_et; 99 if (et->et_active) 100 et->et_event_cb(et, et->et_arg); 101 return (1); 102} 103 104/* 105 * Event timer start method. 106 */ 107static int 108ia64_clock_start(struct eventtimer *et, struct bintime *first, 109 struct bintime *period) 110{ 111 u_long itc, load; 112 register_t is; 113 114 if (period != NULL) { 115 PCPU_SET(md.clock_mode, CLOCK_ET_PERIODIC); 116 load = (et->et_frequency * (period->frac >> 32)) >> 32; 117 if (period->sec > 0) 118 load += et->et_frequency * period->sec; 119 } else { 120 PCPU_SET(md.clock_mode, CLOCK_ET_ONESHOT); 121 load = 0; 122 } 123 124 PCPU_SET(md.clock_load, load); 125 126 if (first != NULL) { 127 load = (et->et_frequency * (first->frac >> 32)) >> 32; 128 if (first->sec > 0) 129 load += et->et_frequency * first->sec; 130 } 131 132 is = intr_disable(); 133 itc = ia64_get_itc(); 134 ia64_set_itm(itc + load); 135 ia64_set_itv(ia64_clock_xiv); 136 ia64_srlz_d(); 137 intr_restore(is); 138 return (0); 139} 140 141/* 142 * Event timer stop method. 143 */ 144static int 145ia64_clock_stop(struct eventtimer *et) 146{ 147 148 ia64_set_itv((1 << 16) | ia64_clock_xiv); 149 ia64_srlz_d(); 150 PCPU_SET(md.clock_mode, CLOCK_ET_OFF); 151 PCPU_SET(md.clock_load, 0); 152 return (0); 153} 154 155/* 156 * We call cpu_initclocks() on the APs as well. It allows us to 157 * group common initialization in the same function. 158 */ 159void 160cpu_initclocks() 161{ 162 163 ia64_clock_stop(NULL); 164 if (PCPU_GET(cpuid) == 0) 165 cpu_initclocks_bsp(); 166 else 167 cpu_initclocks_ap(); 168} 169 170static void 171clock_configure(void *dummy) 172{ 173 struct eventtimer *et; 174 u_long itc_freq; 175 176 ia64_clock_xiv = ia64_xiv_alloc(PI_REALTIME, IA64_XIV_IPI, 177 ia64_ih_clock); 178 if (ia64_clock_xiv == 0) 179 panic("No XIV for clock interrupts"); 180 181 itc_freq = (u_long)ia64_itc_freq() * 1000000ul; 182 183 et = &ia64_clock_et; 184 et->et_name = "ITC"; 185 et->et_flags = ET_FLAGS_PERIODIC | ET_FLAGS_ONESHOT | ET_FLAGS_PERCPU; 186 et->et_quality = 1000; 187 et->et_frequency = itc_freq; 188 et->et_min_period.sec = 0; 189 et->et_min_period.frac = (0x8000000000000000ul / (u_long)(10*hz)) << 1; 190 et->et_max_period.sec = 0xffffffff; 191 et->et_max_period.frac = ((0xfffffffeul << 32) / itc_freq) << 32; 192 et->et_start = ia64_clock_start; 193 et->et_stop = ia64_clock_stop; 194 et->et_priv = NULL; 195 et_register(et); 196 197#ifndef SMP 198 ia64_timecounter.tc_frequency = itc_freq; 199 tc_init(&ia64_timecounter); 200#endif 201} 202SYSINIT(clkcfg, SI_SUB_CONFIGURE, SI_ORDER_SECOND, clock_configure, NULL); 203