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