1139790Simp/*- 2223528Smarcel * Copyright (c) 2005, 2009-2011 Marcel Moolenaar 3145389Smarcel * All rights reserved. 466458Sdfr * 566458Sdfr * Redistribution and use in source and binary forms, with or without 666458Sdfr * modification, are permitted provided that the following conditions 766458Sdfr * are met: 8145389Smarcel * 966458Sdfr * 1. Redistributions of source code must retain the above copyright 1066458Sdfr * notice, this list of conditions and the following disclaimer. 1166458Sdfr * 2. Redistributions in binary form must reproduce the above copyright 1266458Sdfr * notice, this list of conditions and the following disclaimer in the 1366458Sdfr * documentation and/or other materials provided with the distribution. 1466458Sdfr * 15145389Smarcel * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16145389Smarcel * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17145389Smarcel * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18145389Smarcel * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19145389Smarcel * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20145389Smarcel * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21145389Smarcel * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22145389Smarcel * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23145389Smarcel * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24145389Smarcel * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 2566458Sdfr */ 2666458Sdfr 27118414Smarcel#include <sys/cdefs.h> 28118414Smarcel__FBSDID("$FreeBSD$"); 2966458Sdfr 3066458Sdfr#include <sys/param.h> 3166458Sdfr#include <sys/kernel.h> 32205234Smarcel#include <sys/bus.h> 33270296Semaste#include <sys/efi.h> 34205234Smarcel#include <sys/interrupt.h> 35205234Smarcel#include <sys/priority.h> 36223526Smarcel#include <sys/proc.h> 3766458Sdfr#include <sys/queue.h> 3866458Sdfr#include <sys/sysctl.h> 3966458Sdfr#include <sys/systm.h> 40223526Smarcel#include <sys/timeet.h> 4166458Sdfr#include <sys/timetc.h> 42118414Smarcel#include <sys/pcpu.h> 4366458Sdfr 44118414Smarcel#include <machine/cpu.h> 45205234Smarcel#include <machine/intr.h> 46205234Smarcel#include <machine/intrcnt.h> 47200889Smarcel#include <machine/md_var.h> 48205665Smarcel#include <machine/smp.h> 4966458Sdfr 50223526Smarcel#define CLOCK_ET_OFF 0 51223526Smarcel#define CLOCK_ET_PERIODIC 1 52223526Smarcel#define CLOCK_ET_ONESHOT 2 53118414Smarcel 54223526Smarcelstatic struct eventtimer ia64_clock_et; 55205234Smarcelstatic u_int ia64_clock_xiv; 56205234Smarcel 5792667Speter#ifndef SMP 58118414Smarcelstatic timecounter_get_t ia64_get_timecount; 5966458Sdfr 6066458Sdfrstatic struct timecounter ia64_timecounter = { 6166458Sdfr ia64_get_timecount, /* get_timecount */ 6266458Sdfr 0, /* no poll_pps */ 63118414Smarcel ~0u, /* counter_mask */ 6466458Sdfr 0, /* frequency */ 65115178Smarcel "ITC" /* name */ 6666458Sdfr}; 6766458Sdfr 68205234Smarcelstatic u_int 69118414Smarcelia64_get_timecount(struct timecounter* tc) 70118414Smarcel{ 71118414Smarcel return ia64_get_itc(); 72118414Smarcel} 7392667Speter#endif 7466458Sdfr 75205234Smarcelstatic u_int 76205234Smarcelia64_ih_clock(struct thread *td, u_int xiv, struct trapframe *tf) 77205234Smarcel{ 78223526Smarcel struct eventtimer *et; 79268195Smarcel struct trapframe *stf; 80223526Smarcel uint64_t itc, load; 81223526Smarcel uint32_t mode; 82205234Smarcel 83205234Smarcel PCPU_INC(md.stats.pcs_nclks); 84223526Smarcel intrcnt[INTRCNT_CLOCK]++; 85205234Smarcel 86223526Smarcel itc = ia64_get_itc(); 87223526Smarcel PCPU_SET(md.clock, itc); 88205234Smarcel 89223526Smarcel mode = PCPU_GET(md.clock_mode); 90223526Smarcel if (mode == CLOCK_ET_PERIODIC) { 91223526Smarcel load = PCPU_GET(md.clock_load); 92223526Smarcel ia64_set_itm(itc + load); 93223526Smarcel } else 94223526Smarcel ia64_set_itv((1 << 16) | xiv); 95224114Smarcel 96224114Smarcel ia64_set_eoi(0); 97223526Smarcel ia64_srlz_d(); 98205234Smarcel 99223526Smarcel et = &ia64_clock_et; 100268195Smarcel if (et->et_active) { 101268195Smarcel stf = td->td_intr_frame; 102268195Smarcel td->td_intr_frame = tf; 103223526Smarcel et->et_event_cb(et, et->et_arg); 104268195Smarcel td->td_intr_frame = stf; 105268195Smarcel } 106224114Smarcel return (1); 107223526Smarcel} 108205665Smarcel 109223526Smarcel/* 110223526Smarcel * Event timer start method. 111223526Smarcel */ 112223526Smarcelstatic int 113247463Smavia64_clock_start(struct eventtimer *et, sbintime_t first, sbintime_t period) 114223526Smarcel{ 115223526Smarcel u_long itc, load; 116223526Smarcel register_t is; 117223526Smarcel 118247463Smav if (period != 0) { 119223526Smarcel PCPU_SET(md.clock_mode, CLOCK_ET_PERIODIC); 120247463Smav load = (et->et_frequency * period) >> 32; 121205665Smarcel } else { 122223526Smarcel PCPU_SET(md.clock_mode, CLOCK_ET_ONESHOT); 123223526Smarcel load = 0; 124205234Smarcel } 125205665Smarcel 126223526Smarcel PCPU_SET(md.clock_load, load); 127223526Smarcel 128247463Smav if (first != 0) 129247463Smav load = (et->et_frequency * first) >> 32; 130223526Smarcel 131223526Smarcel is = intr_disable(); 132223526Smarcel itc = ia64_get_itc(); 133223526Smarcel ia64_set_itm(itc + load); 134223526Smarcel ia64_set_itv(ia64_clock_xiv); 135223526Smarcel ia64_srlz_d(); 136223526Smarcel intr_restore(is); 137205234Smarcel return (0); 138205234Smarcel} 139205234Smarcel 14066458Sdfr/* 141223526Smarcel * Event timer stop method. 14266458Sdfr */ 143223526Smarcelstatic int 144223526Smarcelia64_clock_stop(struct eventtimer *et) 145223526Smarcel{ 146223526Smarcel 147223526Smarcel ia64_set_itv((1 << 16) | ia64_clock_xiv); 148223526Smarcel ia64_srlz_d(); 149223526Smarcel PCPU_SET(md.clock_mode, CLOCK_ET_OFF); 150223526Smarcel PCPU_SET(md.clock_load, 0); 151223526Smarcel return (0); 152223526Smarcel} 153223526Smarcel 154223526Smarcel/* 155223526Smarcel * We call cpu_initclocks() on the APs as well. It allows us to 156223526Smarcel * group common initialization in the same function. 157223526Smarcel */ 15866458Sdfrvoid 15966458Sdfrcpu_initclocks() 16066458Sdfr{ 161223526Smarcel 162223526Smarcel ia64_clock_stop(NULL); 163223526Smarcel if (PCPU_GET(cpuid) == 0) 164223526Smarcel cpu_initclocks_bsp(); 165223526Smarcel else 166223526Smarcel cpu_initclocks_ap(); 167223526Smarcel} 168223526Smarcel 169223526Smarcelstatic void 170223526Smarcelclock_configure(void *dummy) 171223526Smarcel{ 172223526Smarcel struct eventtimer *et; 173200889Smarcel u_long itc_freq; 17466458Sdfr 175205665Smarcel ia64_clock_xiv = ia64_xiv_alloc(PI_REALTIME, IA64_XIV_IPI, 176205234Smarcel ia64_ih_clock); 177205234Smarcel if (ia64_clock_xiv == 0) 178205234Smarcel panic("No XIV for clock interrupts"); 179205234Smarcel 180200889Smarcel itc_freq = (u_long)ia64_itc_freq() * 1000000ul; 18166486Sdfr 182223526Smarcel et = &ia64_clock_et; 183223526Smarcel et->et_name = "ITC"; 184223526Smarcel et->et_flags = ET_FLAGS_PERIODIC | ET_FLAGS_ONESHOT | ET_FLAGS_PERCPU; 185223526Smarcel et->et_quality = 1000; 186223526Smarcel et->et_frequency = itc_freq; 187247463Smav et->et_min_period = SBT_1S / (10 * hz); 188247463Smav et->et_max_period = (0xfffffffeul << 32) / itc_freq; 189223526Smarcel et->et_start = ia64_clock_start; 190223526Smarcel et->et_stop = ia64_clock_stop; 191223526Smarcel et->et_priv = NULL; 192223526Smarcel et_register(et); 19366458Sdfr 19492667Speter#ifndef SMP 195200889Smarcel ia64_timecounter.tc_frequency = itc_freq; 19666458Sdfr tc_init(&ia64_timecounter); 19792667Speter#endif 19866458Sdfr} 199223526SmarcelSYSINIT(clkcfg, SI_SUB_CONFIGURE, SI_ORDER_SECOND, clock_configure, NULL); 200